Java并发系列(1)——线程状态/生命周期

本文主要讲线程在基于synchronized关键字,以及调用Thread类各个方法,各种情况下的一些状态。

线程有哪些状态?

Thread类中有一个枚举State,描述线程的6个状态:

public enum State {
	NEW,
	RUNNABLE,
	WAITING,
	TIMED_WAITING,
	BLOCKED,
	TERMINATED
}

线程状态是如何改变的?

这里结合synchronized关键字,写的一个例子:

public static void main(String[] args) throws InterruptedException {
    Object object = new Object();
    List<Thread> list = new ArrayList<>();
    for (int i = 0; i < 5; i++) {
        /**
         * 初始化线程对象,会设置线程组,父线程,线程优先级,是否是daemon线程
         */
        Thread t = new Thread(() -> {
            /**
             * 第三步:
             * 线程需要等待获取到锁,此时线程状态是 BLOCKED
             * 并将线程对象加入到synchronized锁内部的entrySet中
             * 详见子路老师的博客:https://blog.youkuaiyun.com/java_lyvee/article/details/110996764
             * PS:其实关于entrySet这个概念,我在jdk官方文档并没有找到,这里存质疑
             */
            synchronized (object) {
                /**
                 * 第六步:
                 * 各个线程都获取到了锁,并开始执行任务,此时状态是 RUNNABLE
                 * 注意:最后获取到锁的线程,反而最先执行!因为synchronized锁内部维护了一个entrySet,遵循后进先出的原则
                 */
                System.out.println(Thread.currentThread().getName() + "开始任务..." + Thread.currentThread().getState().name());
                try {
                    /**
                     * 第七步:
                     * 各个线程开始释放锁,此时线程状态是 WAITING
                     * 此时线程对象会被加入到synchronized维护的waitSet中
                     * 详见jdk官方文档:https://docs.oracle.com/javase/specs/jls/se12/html/jls-17.html
                     */
                    object.wait();
                } catch (InterruptedException e) {
                    /**
                     * 第十一步:
                     * 获取到锁,捕捉到中断异常,此时线程状态是: RUNNABLE
                     */
                }
                System.out.println(Thread.currentThread().getName() + "结束任务..." + Thread.currentThread().getState().name());
            }
        }, "线程" + i);
        list.add(t);
    }
    System.out.println();
    /**
     * 第一步
     * main线程获取到锁
     */
    synchronized (object) {
        for (Thread t : list) {
            /**
             * 第二步:
             * new了线程对象,那么此时线程状态是 NEW
             */
            System.out.println("启动前" + t.getName() + "状态:" + t.getState().name());
            /**
             * 启动线程,如果线程状态不是NEW,则会报IllegalThreadStateException异常
             * 调用本地方法start0() ----可下载openjdk源码,方法调用具体位置:src/hotspot/share/prims/jvm.cpp JVM_ENTRY()方法
             *    1.在C++层面会做一些校验,设置线程栈大小
             *    2.真正意义上创建一个线程(向操作系统申请创建线程)
             *    3.启动线程
             */
            t.start();
            /**
             * 由于启动线程,需要向操作系统申请创建线程,
             * 虽然操作系统的线程调度,是纳秒级别,但还是有可能新申请创建的线程,会延后才能真正创建好
             * 甚至有可能说,在main线程释放锁之后,操作系统的线程才被创建好,就可能状态直接会到 RUNNABLE
             * 故这里在debug的时候,可以进行一个睡眠
             * 读者可以屏蔽此代码,多增加线程数量进行测试,挺有意思的~
             */
            TimeUnit.NANOSECONDS.sleep(1);
            /**
             * 第三步:
             * 由于main线程已经获取到锁,5个子线程的run()都在等待获取锁,所以这时候线程状态是 BLOCKED
             */
            System.out.println("启动后" + t.getName() + "线程状态-------" + t.getState().name());
        }
        /**
         * 第四步,main线程执行完synchronized代码块之后,释放锁
         */
    }
    System.out.println();
    /**
     * 第八步:
     * main线程再次获取到锁
     */
    synchronized (object) {
        System.out.println();
        for (Thread t : list) {
            System.out.println("中断前" + t.getName() + "状态:" + t.getState().name());
            /**
             * 第九步:
             * 调用线程中断方法,此时线程状态 WAITING -> BLOCKED
             * 这里并没有使用 object.notify()或者object.notifyAll()唤醒线程
             * 因为这两个方法唤醒的线程是无法掌控的
             */
            t.interrupt();
            /**
             * 由于 interrupt() 也涉及到操作系统到线程调度,这里也需要短暂睡眠,否则线程状态无法及时更新打印
             */
            TimeUnit.NANOSECONDS.sleep(1);
            System.out.println("中断后" + t.getName() + "线程状态-------" + t.getState().name());
        }
        System.out.println();
        /**
         * 第十步,main线程执行完synchronized代码块之后,释放锁
         */
    }
}

运行结果:

启动前线程0状态:NEW
启动后线程0线程状态-------BLOCKED
启动前线程1状态:NEW
启动后线程1线程状态-------BLOCKED
启动前线程2状态:NEW
启动后线程2线程状态-------BLOCKED
启动前线程3状态:NEW
启动后线程3线程状态-------BLOCKED
启动前线程4状态:NEW
启动后线程4线程状态-------BLOCKED

线程4开始任务...RUNNABLE
线程3开始任务...RUNNABLE
线程2开始任务...RUNNABLE
线程1开始任务...RUNNABLE
线程0开始任务...RUNNABLE

中断前线程0状态:WAITING
中断后线程0线程状态-------BLOCKED
中断前线程1状态:WAITING
中断后线程1线程状态-------BLOCKED
中断前线程2状态:WAITING
中断后线程2线程状态-------BLOCKED
中断前线程3状态:WAITING
中断后线程3线程状态-------BLOCKED
中断前线程4状态:WAITING
中断后线程4线程状态-------BLOCKED

线程4结束任务...RUNNABLE
线程3结束任务...RUNNABLE
线程2结束任务...RUNNABLE
线程1结束任务...RUNNABLE
线程0结束任务...RUNNABLE

如果读者有心看到此篇文章,建议把这个demo拷贝下来,多多进行测试和调试,把线程数设置大一些,比如50或者100,这里我是为了方便打印显示日志才设置的线程数为5.

线程状态图解

最终还是得来一张图作为总结:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值