Java多线程02——线程的生命周期和状态调度

2023-10-16 10:57:35 浏览数 (1)

1 线程的生命周期

在线程的生命周期中,要经过新建​​new​​​、就绪​​runnable​​​、运行​​running​​​、阻塞​​blocked​​​和死亡​​dead​​ 5种状态。

当线程启动后,它不可能一直“霸占”CPU独自运行,所以CPU需要在多条线程之间切换,于是线程状态也会多次在运行和阻塞之间切换。

线程的生命周期五个阶段

1、新建状态,当程序使用​​new​​关键字创建了一个线程之后,该线程处于新建状态,此时仅由JVM为其分配内存,并初始化其成员变量的值。

2、就绪状态,当线程对象调用了​​start​​方法后,该线程处于就绪状态。java虚拟机会为其创建方法调用栈和程序计数器,等待调试运行。

3、运行状态,如果处于就绪状态的线程获得了CPU,开始执行run方法的线程执行体,则该线程处于运行状态。

4、阻塞状态,当处于运行状态的线程失去所占用资源之后,便进入阻塞状态。

5、死亡状态,在线程的生命周期当中,线程执行完成之后的最终状态。

2 线程的休眠

线程休眠的方法是 ​​Thread.sleep(long millis)​

线程休眠的目的是使线程让出CPU的最简单做法之一,线程休眠的时候,会将CPU资源交给其他线程,以便能轮换执行,当休眠一定时间后,线程会苏醒,进入就绪状态等待执行。

简单说,哪个线程调用​​sleep()​​方法,就休眠哪个线程。

3 线程的让步

​yield()​​的作用是让步。它能让当前线程由“运行状态”进入到“就绪状态”,从而让其它具有相同优先级的等待线程获取执行权。

但是,并不能保证在当前线程调用​​yield()​​之后,其它具有相同优先级的线程就一定能获得执行权;也有可能当前线程又进入到“运行状态”继续运行。

简单的说,就是哪个线程执行​​yield​​方法,就是礼让。

代码语言:javascript复制
public class UserThread extends Thread {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()   " :A");

        Thread.yield();

        System.out.println(Thread.currentThread().getName()   " :B");
    }
}

4 线程的合并

线程的合并作用,就是将几个并行线程合并为一个单线程执行。

当一个线程必须等待另一个线程执行完毕才能执行时,可以使用​​join()​​方法。

代码语言:javascript复制
public class UserThread extends Thread {
    @Override
    public void run() {
        for(int i=0;i<3;i  ){
            System.out.println(Thread.currentThread().getName()   "执行"   i   "次");
        }
    }
}
public class Test {
    public static void main(String[] args) {
        UserThread userThread = new UserThread();
        userThread.start();

        for(int i=0;i<5;i  ){
            System.out.println(Thread.currentThread().getName()   "执行"   i   "次");

            //将子线程合并到主线程中
            try {
                userThread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

5 守护线程

调用线程对象的方法 ​​setDaemon(true)​​ ,可以将其设置为守护线程。

JVM的垃圾回收、内存管理等线程都是守护线程。

​setDaemon(boolean on)​​ 将该线程标记为守护线程或用户线程,该方法必须在启动线程前调用。

当正在运行的线程都是守护线程时,java虚拟机就会退出。

代码语言:javascript复制
public class Backend extends Thread {
    @Override
    public void run() {
        for(int i=0;i<8888;i  ){
            System.out.println("后端执行"   i   "次");
        }
    }
}
public class Front extends Thread {
    @Override
    public void run() {
        for(int i=0;i<3;i  ){
            System.out.println("前端执行"   i   "次");
        }
    }
}
public class Test {
    public static void main(String[] args) {
        Backend backend = new Backend();
        Front front = new Front();

        //设置后台为守护线程
        backend.setDaemon(true);

        backend.start();
        front.start();
    }
}

当非守护线程执行完成后,jvm就会停止运行。

6 线程的中断和死亡

6.1 线程中断

线程中断就是让目标线程停止执行,但它不会使线程立即终止,而是给线程发送一个通知,告诉线程JVM希望退出执行,至于目标线程何时退出,完全由JVM自己决定。

代码语言:javascript复制
public class UserThread {
    public static void main(String[] args) {
        Thread thread = new Thread(){
            @Override
            public void run() {
                while (true){
                    //判断当前线程是否处于中断状态
                    if(this.isInterrupted()){
                        System.out.println(Thread.currentThread().getName()   " 处于中断状态");
                        break;
                    }
                }
            }
        };

        thread.start();

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //调用线程的中断方法
        thread.interrupt();
    }
}

6.2 线程死亡

线程会以如下三种方式结束,结束后就处于死亡状态:

  • run() 或 ​​call()​​ 方法执行完成,线程正常结束;
  • 线程抛出一个未捕获的 ​​Exception​​​ 或 ​​Error​​;
  • 直接调用线程的​​stop()​​方法结束线程,该方法容易导致死锁,不建议使用

注意:

不能对一个已经死亡的线程调用​​start()​​方法,因为死亡线程已不再可用。

代码语言:javascript复制
public class Test {
    //线程退出标记
    public static boolean exit = false;

    public static void main(String[] args) {
        new Thread(){
            @Override
            public void run() {
                System.out.println("线程启动了");
                while (!exit){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("线程结束了");
            }
        }.start();

        //等待5秒,修改线程结束标记
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        exit = true;
    }
}

0 人点赞