线程的生命周期

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 状态

Blocked 与 Waiting 的区别

  • 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 状态

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/111804.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JWT安全及案例实战

文章目录 JWT 安全1. Cookie2. Session3. Token4. JWT4.1 JWT概述4.1.1 JWT头4.1.2 有效载荷4.1.3 签名哈希4.1.4 通信流程 4.2 JWT 漏洞描述4.3 JWT 漏洞原理4.4 JWT 安全防御 5. WebGoat 靶场实验5.1 第四关5.2 第五关5.3 第七关 越权与逻辑漏洞 Web漏洞点只有一个入口&#…

Redis五大基本数据类型

1、字符串类型 字符串类型相当于 java 中的 String 类型。Redis 中的 String 类型以二进制方式存储,不会做任何的编码转换,因此不仅仅可以存储文本数据、整数、普通的字符串、JSON、xml文件,还可以存储图片、视频、音频。String 存储的种类虽…

【踩坑篇】代码中使用 Long 作为 Map的Key存在的问题

本周的工作结束&#xff0c;详述一些在项目代码中实际遇到的一些坑。 代码中遇到这样一个场景&#xff1a; 有个业务接口&#xff0c;接口返回的值是一个JSON格式的字符串&#xff0c;通过JSON解析的方式&#xff0c;解析为格式为&#xff1a; Map<Long, Map<String, O…

【物联网】常见电子元器件(电阻、电容、电感、二极管、三极管)综合,详细分析原理及其应用

电子元器件是现代电子技术的基础&#xff0c;它们在各个领域中发挥着重要作用。从三极管到电容器、电阻器&#xff0c;这些常用元器件承担着放大、开关、滤波等关键任务。它们的特性和组合方式决定了电路的性能和功能。本文将介绍常用电子元器件的工作原理和应用场景&#xff0…

Lua学习笔记:在Visual Studio中调试Lua源码和打断点

前言 本篇在讲什么 调试Lua源码 本篇需要什么 对Lua语法有简单认知 依赖Visual Studio工具 本篇的特色 具有全流程的图文教学 重实践&#xff0c;轻理论&#xff0c;快速上手 提供全流程的源码内容 ★提高阅读体验★ &#x1f449; ♠ 一级标题 &#x1f448; &…

git中无法使用方向键的问题

windows下使用git命令行执行react脚本安装&#xff0c;发现无法使用上下键来去选中选项。最后只能换成cmd命令执行&#xff0c;发现可以上下移动以选中需要的选项。 bash命令行&#xff1a;移动光标无法移动选项 cmd命令行

自然语言处理(一):基于统计的方法表示单词

文章目录 1. 共现矩阵2. 点互信息3. 降维&#xff08;奇异值分解&#xff09; 1. 共现矩阵 将一句话的上下文大小窗口设置为1&#xff0c;用向量来表示单词频数&#xff0c;如&#xff1a; 将每个单词的频数向量求出&#xff0c;得到如下表格&#xff0c;即共现矩阵&#x…

2023!6招玩转 Appium 自动化测试

Appium是个什么鬼 Appium是一个移动端的自动化框架&#xff0c;可用于测试原生应用&#xff0c;移动网页应用和混合型应用&#xff0c;且是跨平台的。可用于IOS和Android以及firefox的操作系统。原生的应用是指用android或ios的sdk编写的应用&#xff0c;移动网页应用是指网页…

DevEco Studio中如何设置HarmonyOS/OpenHarmony应用开发

DevEco Studio内置有帮助中心&#xff0c;初学HarmonyOS 及OpenHarmony应用、元服务的开发者&#xff0c;通过内置的帮助中去系统的学习相关内容&#xff0c;是边练边学&#xff0c;快速上手的最佳方式。 一、帮助 二、快速开始 三、HarmonyOS应用、元服务开发相关 四、OpenHa…

扔掉你的开发板,跟我玩Mcore-全志h616

本文转载自WhyCan Forum(哇酷开发者社区)&#xff1a; https://whycan.com/t_10024.html 作者leefei 这是一个1.69寸触摸小电视。使用全志H616芯片&#xff0c;板上硬件有mpu6050陀螺仪&#xff0c;USB转ttl调试串口&#xff0c;一个USB接口&#xff0c;WIFI&蓝牙&#x…

爬虫代理在数据采集中的应用详解

随着互联网技术的不断发展&#xff0c;数据采集已经成为了各个行业中必不可少的一项工作。在数据采集的过程中&#xff0c;爬虫代理的应用越来越受到了重视。本文将详细介绍爬虫代理在数据采集中的应用。 什么是爬虫代理&#xff1f; 爬虫代理是指利用代理服务器来隐藏真实的IP…

异步FIFO设计的仿真与综合技术(5)

概述 本文主体翻译自C. E. Cummings and S. Design, “Simulation and Synthesis Techniques for Asynchronous FIFO Design 一文&#xff0c;添加了笔者的个人理解与注释&#xff0c;文中蓝色部分为笔者注或意译。前文链接&#xff1a; 异步FIFO设计的仿真与综合技术&#xf…