线程的生命周期及其各种状态的转换(6种状态)

线程的状态,目前网上很多文章停留在五种状态的分析,但是经过翻看thread类的源码发现其实6种状态。最近偶然间看到一篇文章,对于6种状态的转换讲的很好,特此转载过来了。

原文链接:总算把线程六种状态的转换说清楚了!   


1、线程的六种状态

  • New(新创建)

  • Runnable(可运行)

  • Blocked(被阻塞)

  • Waiting(等待)

  • Timed Waiting(计时等待)

  • Terminated(被终止)

在我们程序编码中如果想要确定线程当前的状态,可以通过 getState() 方法来获取,同时我们需要注意任何线程在任何时刻都只能是处于一种状态。

线程状态转换

2、New 新建状态

        New 表示线程被创建但尚未启动的状态:当我们用 new Thread() 新建一个线程时,如果线程没有开始运行 start() 方法,那么线程也就没有开始执行 run() 方法里面的代码,那么此时它的状态就是 New。而一旦线程调用了 start(),它的状态就会从 New 变成 Runnable,进入到图中绿色的方框

3、Runnable 可运行状态

Java 中的 **Runable ** 状态对应操作系统线程状态中的两种状态,分别是 Running 和 Ready,也就是说,Java 中处于 Runnable 状态的线程有可能正在执行,也有可能没有正在执行,正在等待被分配 CPU 资源。
所以,如果一个正在运行的线程是 Runnable 状态,当它运行到任务的一半时,执行该线程的 CPU 被调度去做其他事情,导致该线程暂时不运行,它的状态依然不变,还是 Runnable,因为它有可能随时被调度回来继续执行任务。

4、阻塞状态

 Blocked(被阻塞)、Waiting(等待)、Timed Waiting(计时等待) 三个状态统称为阻塞状态。

4.1、Blocked 被阻塞状态

首先我们来认识一下 Blocked 状态,这是一个相对简单的状态,我们可以通过下面的图示看到,从 Runnable 状态进入到 Blocked 状态只有一种途径,那么就是当进入到 synchronized 代码块中时未能获得相应的 monitor 锁(关于 monitor 锁我们在之后专门来介绍,这里我们知道 synchronized 的实现都是基于 monitor 锁的)

被阻塞状态

有连接线从 Blocked 状态指向了 Runnable ,也只有一种情况,那么就是当线程获得 monitor 锁,此时线程就会进入 Runnable 状体中参与 CPU 资源的抢夺

4.2、Waiting 等待状态

对于 Waiting 状态的进入有三种情况,如下图中所示,分别为:

  • 当线程中调用了没有设置 Timeout 参数的 Object.wait() 方法
  • 当线程调用了没有设置 Timeout 参数的 Thread.join() 方法
  • 当线程调用了 LockSupport.park() 方法

waiting状态

关于 LockSupport.park() 方法,这里说一下,我们通过上面知道 Blocked 是针对 synchronized monitor 锁的,但是在 Java 中实际是有很多其他锁的,比如 ReentrantLock 等,在这些锁中,如果线程没有获取到锁则会直接进入 Waiting 状态,其实这种本质上它就是执行了 LockSupport.park() 方法进入了Waiting 状态
**Blocked **与 **Waiting** 的区别

  • Blocked 是在等待其他线程释放 monitor 锁
  • Waiting 则是在等待某个条件,比如 join 的线程执行完毕,或者是 notify()/notifyAll() 。

4.3、Timed Waiting 计时等待状态

 Timed Waiting 状态,它与 Waiting 状态非常相似,其中的区别只在于是否有时间的限制,在 Timed Waiting 状态时会等待超时,之后由系统唤醒,或者也可以提前被通知唤醒如 notify

计时等待状态

通过上述图我们可以看到在以下情况会让线程进入 Timed Waiting 状态。

  • 线程执行了设置了时间参数的 Thread.sleep(long millis) 方法;
  • 线程执行了设置了时间参数的 Object.wait(long timeout) 方法;
  • 线程执行了设置了时间参数的 Thread.join(long millis) 方法;
  • 线程执行了设置了时间参数的 LockSupport.parkNanos(long nanos) 方法和 LockSupport.parkUntil(long deadline) 方法。

通过这个我们可以进一步看到它与 waiting 状态的相同

5、线程状态间转换

接下来我们将来分析各自状态之间的转换,其实主要就是 Blocked、waiting、Timed Waiting 三种状态的转换 ,以及他们是如何进入下一状态最终进入 Runnable

5.1、Blocked 进入 Runnable

想要从 Blocked 状态进入 Runnable 状态,我们上面说过必须要线程获得 monitor 锁,但是如果想进入其他状态那么就相对比较特殊,因为它是没有超时机制的,也就是不会主动进入。

blocked进入runnable

5.2、Waiting 进入 Runnable

只有当执行了 LockSupport.unpark(),或者 join 的线程运行结束,或者被中断时才可以进入 Runnable 状态。

waiting进入runnable

如果通过其他线程调用 notify() 或 notifyAll()来唤醒它,则它会直接进入 Blocked 状态,这里大家可能会有疑问,不是应该直接进入 Runnable 吗?这里需要注意一点 ,因为唤醒 Waiting 线程的线程如果调用 notify() 或 notifyAll(),要求必须首先持有该 monitor 锁,这也就是我们说的 wait()、notify 必须在 synchronized 代码块中。
所以处于 Waiting 状态的线程被唤醒时拿不到该锁,就会进入 Blocked 状态,直到执行了 notify()/notifyAll() 的唤醒它的线程执行完毕并释放 monitor 锁,才可能轮到它去抢夺这把锁,如果它能抢到,就会从 Blocked 状态回到 Runnable 状态。

1

这里大家一定要注意这点,当我们通过 notify 唤醒时,是先进入阻塞状态的 ,再等抢夺到 monitor 锁喉才会进入 Runnable 状态!

5.3、Timed Waiting 进入 Runnable

同样在 Timed Waiting 中执行 notify() 和 notifyAll() 也是一样的道理,它们会先进入 Blocked 状态,然后抢夺锁成功后,再回到 Runnable 状态。

11

但是对于 Timed Waiting 而言,它存在超时机制,也就是说如果超时时间到了那么就会系统自动直接拿到锁,或者当 join 的线程执行结束/调用了LockSupport.unpark()/被中断等情况都会直接进入 Runnable 状态,而不会经历 Blocked 状态

1

6、Terminated 终止

最后我们来说最后一种状态,Terminated 终止状态,要想进入这个状态有两种可能。

  • run() 方法执行完毕,线程正常退出。
  • 出现一个没有捕获的异常,终止了 run() 方法,最终导致意外终止。

注意:

  • 线程的状态是按照箭头方向来走的,比如线程从 New 状态是不可以直接进入 Blocked 状态的,它需要先经历 Runnable 状态。
  • 线程生命周期不可逆:一旦进入 Runnable 状态就不能回到 New 状态;一旦被终止就不可能再有任何状态的变化。
  • 所以一个线程只能有一次 New 和 Terminated 状态,只有处于中间状态才可以相互转换。也就是这两个状态不会参与相互转化
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值