线程的生命周期

CV大师--好文章的搬运工

知乎有质量的文章是真多

CSDN鱼龙混杂

大部分是鱼

原文章:线程的生命周期及其六种状态的转换 - 知乎 (zhihu.com)


线程的生命周期

线程的生命周期主要有以下六种状态:

  • New(新创建)
  • Runnable(可运行)
  • Blocked(被阻塞)
  • Waiting(等待)
  • Timed Waiting(计时等待)
  • Terminated(被终止)

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

1.New 新建状态

首先我们展示一下整个线程状态的转换流程图,下面我们将进行详细的介绍讲解,如下图所示,我们可以直观的看到六种状态的转换,首先左侧上方是 NEW 状态,这是创建新线程的状态,相当于我们 new Thread() 的过程。

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

2.Runnable 可运行状态

Java 中的 Runable 状态对应操作系统线程状态中的两种状态,分别是 Running 和 Ready,也就是说,Java 中处于 Runnable 状态的线程有可能分到CPU的时间片了正在执行,也有可能在等待自己的时间片,处于等待执行。

所以,如果一个正在运行的线程是 Runnable 状态,当它运行到任务的一半时,执行该线程的 CPU 被调度去做其他事情,导致该线程暂时不运行,它的状态依然不变,还是 Runnable,因为它有可能随时被调度回来继续执行任务。

阻塞状态

上面认识了线程的关键状态 Runnable ,那么接下来我们来看一下下面的三个状态,这三个状态我们可以统称为阻塞状态,它们分别是 Blocked(被阻塞)、Waiting(等待)、Timed Waiting(计时等待) .

3.Blocked 被阻塞状态

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

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

4.Waiting 等待状态

上面我们看完阻塞状态,那么接下来我们了解一下 Waiting 状态,对于 Waiting 状态的进入有三种情况,如下图中所示,分别为:

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

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

BlockedWaiting 的区别

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

5.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 状态的相同

6.Terminated 终止

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

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

线程状态间转换

上面我们讲了各自状态的特点和运行状态进入相应状态的情况 ,那么接下来我们将来分析各自状态之间的转换,其实主要就是 Blocked、waiting、Timed Waiting 三种状态的转换 ,以及他们是如何进入下一状态最终进入 Runnable

1.Blocked 进入 Runnable

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

如下图中紫色加粗表示线路:

2.Waiting 进入 Runnable

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

如下图标注

如果通过其他线程调用 notify() 或 notifyAll()来唤醒它,则它会直接进入 Blocked 状态,这里大家可能会有疑问,不是应该直接进入 Runnable 吗?这里需要注意一点 ,因为唤醒 Waiting 线程的线程如果调用 notify() 或 notifyAll(),要求必须首先持有该 monitor 锁,这也就是我们说的 wait()、notify 必须在 synchronized 代码块中。

所以处于 Waiting 状态的线程被唤醒时拿不到该锁,就会进入 Blocked 状态,直到执行了 notify()/notifyAll() 的唤醒它的线程执行完毕并释放 monitor 锁,才可能轮到它去抢夺这把锁,如果它能抢到,就会从 Blocked 状态回到 Runnable 状态。

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

3.Timed Waiting进入Runnable

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

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

相关推荐
懒鸟一枚2 小时前
深入理解 Linux 内存、Swap 交换分区与分页机制的关系
java·linux·数据库
我命由我123454 小时前
执行 Gradle 指令报错,无法将“grep”项识别为 cmdlet、函数、脚本文件或可运行程序的名称
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
考虑考虑4 小时前
Sentinel安装
java·后端·微服务
凤凰院凶涛QAQ5 小时前
《Java版数据结构 & 集合类剖析》栈与队列:“push/pop 是栈的灵魂,offer/poll 是队列的骨架——四组 API,两种人生”
java·开发语言·数据结构
掘金_答案6 小时前
上线那天,一个 ConcurrentHashMap 差点送走我的 AI 客服——3 天排查 JVM 血泪史
java·后端·架构
猿与禅7 小时前
CosId 分布式 ID 生成器完全教程:从架构原理到生产落地
java·shardingsphere·雪花算法·分布式id·高性能·cosid·号段模式
MindUp7 小时前
企业网盘权限模型解析:多层级访问控制与审计能力选型指南
java·linux·运维
NG4777 小时前
【软件设计与体系结构】-策略设计模式
java·设计模式·软件工程
程序员天天困7 小时前
优雅记录操作日志:从注解到 SpEL 的全链路实践与开源方案对比
java·后端
战血石LoveYY8 小时前
Integer类型超限,导致数据异常
java·算法