Java中的多线程

线程

程序

  • 计算机为解决某种问题,使用计算机语言编写的一系列指令(代码)的集合。
  • 本章的程序特指的是静态的,安装在硬盘上代码合集。

进程:运行中的程序(被加载到内存中),是操作系统中分配的最小单位;

线程:进程可以进一步细化为线程,是进程内一个最小执行单元,是CPU进行任务调度的最小单位。

进程与线程的关系

  • 一个进程可以包含多个线程

  • 一个线程只能隶属于一个进程,线程也不能脱离进程存在

  • 一个进程中至少有一个线程(即主线程),main方法就是用来启动主线程

  • 在主线程中可以创建并启动其他线程

  • 所有线程共享同一进程内存资源。

早期没有线程,早期CPU执行的时候,是以进程为单位执行,进程单位较大,当一个进程运行时,其他进行就不能运行,所以后来,将进程中的多个任务,细化为线程,CPU执行单位,也从进程转为更小的单位。

需求:想在java程序中有几件不相关的事情同时有机会执行。

可以在java中创建线程,把一些要执行的任务放在线程中执行。

java中创建线程的方式

  • 继承Thread(线程)类,重写Thread类中的run()方法,在run()中来编写我们需要执行的任务代码。

    public class javaThread extends Thread{
        @Override
        public void run(){
            for (int i=0;i<2000000000;i++){
                System.out.println("Thread:"+i);
            }
        }//线程执行任务的方法
    
        public static void main(String[] args) {
            for (int i=0;i<1000000000;i++){
                System.out.println("main:"+i);
            }
            javaThread javathread=new javaThread();
            javathread.start();//启动线程,并不会是立即执行,需要操作系统的调度。切记不能直接调用run()方法,因为run()方法只是一个普通方法调用,并不是启动线程。
        }
    }
    
  • 实现Runnable接口(这个类不能称为线程,是一个任务类)并重写Runnable中的run()方法

public class TaskDemo implements Runnable{
    @Override
    public void run(){
        for (int i=0;i<1000;i++){
            System.out.println("task:"+i);
        }
    }
    public static void main(String[] args) {
        TaskDemo taskDemo=new TaskDemo();
        Thread thread=new Thread(taskDemo);
        Thread thread1=new Thread(taskDemo);
        thread.start();
        thread1.start();
        for (int i=0;i<1000;i++){
            System.out.println("main:"+i);
        }
    }
}

一般第二种用的更多一点,因为可以一次性实现多个接口;

更适合多线程共享同一份资源的场景。

Thread类

构造方法

new Thread(Runnable runnable);//接收一个任务对象
new Thread(Runnable runnable,String)//接收一个任务对象,并为线程设置名字

setName("name");//为我的线程设置名字
getName("name");//获取线程名字

getPriority();//获取线程优先级
setPriority();//设置线程优先级,默认为5,高优先级的线程优先被处理
/*
操作系统任务调度算法:
先来先服务(FCFS)调度算法;
短作业优先(SJF)调度算法;
优先级调度算法;
时间片轮转调度算法;
高响应比优先调度算法;
多级反馈队列调度算法;
*/

sleeep(long m);//让线程休息指定毫秒
join();//让其他线程等待当前线程结束后再执行

线程生命周期,线程从创建到销毁经历:

  • 新建
new Thread(); 处于新建状态,此时还不能被执行
start();启动线程,让线程进入到就绪状态
  • 就绪
获得CPU执行权后,线程进入到CPU执行
  • 运行
运行中的线程可以被切换,回到就绪状态,也可能因为休眠等原因进入到阻塞状态
  • 阻塞
线程休眠时间到了,回到就绪状态
  • 死亡
当线程中所有任务执行完了,线程也自动销毁

守护线程

守护线程也是线程中的一种,区别在于它的结束

如果一个线程是守护线程,那么它会等java中其他的线程任务结束后,自动终止。

守护线程是为其他线程提供服务的,例如jvm中的垃圾回收线程。

多线程

在一个应用程序中,存在多个线程,不同的线程可以并行的执行任务

  • 优点:

提高程序的处理能力,杀毒软件,垃圾清理,病毒查杀

  • 缺点:

线程越多,占用的内存和CPU越多;

多个线程对同一个共享的资源进行访问,会出现线程安全问题。

如何解决多个线程访问同一个共享资源时不出问题?

第一种加锁方法Synchronized

同步=排队+锁(synchronized)

锁的使用规范:

  • 修饰代码
//买票系统
public class Ticket extends Thread{
    static int t=1000;//剩余的票
    static Object object=new Object();
    @Override
    public void run() {
        /*
        synchronized修饰代码块
        同步对象:对多个线程对应的对象必须是同一个
                用来记录有没有线程进入到同步代码块中的
                同步对象可以是java中任何类的对象
                创建了两个卖票的对象,锁有两把
        synchronized(同步对象){
        同步代码块
        }
        */
        synchronized(object){
            while (true){
                try {
                    if(t>0){
                        System.out.println(Thread.currentThread().getName()+"买到了"+t);
                        t--;
                    }else{
                        break;
                    }
                    Thread.sleep(100);
                }catch (Exception e){
                    e.printStackTrace();
                }
        }
        }
    }
}
public class test extends Thread{
    public static void main(String[] args) {
        Ticket ticket=new Ticket();
        ticket.setName("窗口1");
        Ticket ticket1=new Ticket();
        ticket1.setName("窗口2");
        ticket1.start();
        ticket.start();
    }
}
  • 修饰方法

synchronized修饰方法,同步对象会有默认的

修饰的是非静态方法那么同步对象是this

修饰的是静态方法那么同步对象是当前类的Class对象

当前类的Class对象:一个类加载到内存后会为这个类创建一个唯一的Class类的对象。

第二种加锁方法ReentrantLock

  • ReentantLock只能对代码块进行加锁,不能对方法进行加锁
ReentrantLock reentrantlock=new ReentrantLock();//提供一个实现加锁的对象
reentrantLock.lock();//加锁
reentrantLock.unlock();//释放锁

synchronized和ReentrantLock的区别

  • 相同点:都实现了加锁的功能;

  • 不同点:

  1. synchronized是一个关键字,ReentrantLock是一个类
  2. synchronized修饰代码块和方法,ReentrantLock只能修饰代码块
  3. synchronized可以隐式的加锁和释放锁,运行过程中如果出现了异常可以自动释放锁
  4. ReentrantLock可以手动的添加和释放锁,建议在finally代码块中释放锁。

线程通信

wait()

一旦执行此方法,当前的线程就会进入阻塞状态,并释放同步锁对象

让线程等待,自动释放锁,必须等待至其他线程唤醒

notify()

一旦执行此方法,就会唤醒被wait()的一个线程。如果有多个线程被wait,就唤醒优先级高的那个

notifyAll()

一旦执行此方法,就会唤醒所有被wait的线程

你可能感兴趣的:(java,开发语言)