Java 线程的生命周期和状态(实践加深理解)

一、常规回答(八股文)

线程的生命周期一共分为有6个状态,在某个时刻中,一个线程只会处在6个状态中的其中一种。

第1:初始状态(NEW) 当前的线程已经被创建了出来,但是还没有通过调用start()方法来启用。

第2:运行状态(RUNNABLE) 当前的进程已经调用了start()方法,进入了运行的状态,或者正在等待CPU的调度。

第3:阻塞状态(BLOCKED) 当前的线程会暂停执行,等待某些事件的发生,比如等待锁的释放。

第4:等待状态(WAITING) 当前线程会暂停执行,等待其他线程的特定事件。
如果线程调用wait()方法导致暂停,则需其他线程调用notify方法唤醒当前线程;如果线程调用了其他线程的join()方法,则需等待join对应的线程执行完毕后,才能唤醒当前线程。

第5:计时等待状态(TIMED_WAITING) 线程暂停执行,但是在经历了一段时间后会自动被唤醒。如调用Thread.sleep()方法,一段时间内线程被阻塞。

第6:终止状态(TERMINATED) 当前线程的逻辑已经执行完毕,或者遇到了异常而终止,就会进入终止状态。

二、加深理解

加深理解面试题答案的最好方法,无非就是自己实践一次。

所以,为了能够检测到 Java 程序中各个线程的运行状态,这里使用了一个小工具:arthas-boot

2.1 arthas-boot 安装(可跳过)

官方文档:https://alibaba.github.io/arthas
下载连接:https://arthas.aliyun.com/arthas-boot.jar

arthas 实际上是 Alibaba 开源的 Java 诊断工具,它的特点是使用方便,功能强大

最近学习了 JVM 调优相关的内容,使用到了这个工具,所以这里才想到使用它来检测 Java 程序的线程状态信息(如下图),来更好的理解线程在什么时候会进入什么状态。

在这里插入图片描述
这里简单说一下使用步骤,如不想安装则可以跳过直接看:[点击跳转] 2.2 开始检验线程的状态

第1步, 下载 arthas-boot.jar 文件:https://arthas.aliyun.com/arthas-boot.jar

第2步, 在本地系统运行一个Java程序,比如在 IDEA 中写一个死循环并运行:

在这里插入图片描述

第3步, 打开cmd,进入文件所在目录,执行命令

java -jar arthas-boot.jar

在这里插入图片描述

随后选择需要挂载的 Java 程序,这里我们要监控的是 Test3 这个类,所以在控制台输入1并回车。

在这里插入图片描述

等到出现以下 arthas 的图案,就说明运行成功了。

在这里插入图片描述
输入 thread 命令,即可查看当前进程的所有运行的线程:

在这里插入图片描述

2.2 检测线程的状态

2.2.1 初始状态(NEW)

当一个线程被创建但还没有调用start()方法运行的时候,这时的线程状态应该是 NEW

这个状态下的线程用arthas工具获取不到,所以就只能用代码获取了。

测试程序:

public class Test3 {public static void main(String[] args) throws InterruptedException {Thread test1 = new Thread(() -> {while (true) {// 模拟线程在执行某些操作}});test1.setName("test1");System.out.printf("线程[%s]当前的状态是: %s\n", test1.getName(), test1.getState());}
}

运行结果:

在这里插入图片描述

2.2.2 运行状态(RUNNABLE)

当一个线程被创建了,且调用了start()方法后,该线程状态应为RUNNABLE

测试程序:

public class Test3 {public static void main(String[] args) throws InterruptedException {Thread test1 = new Thread(() -> {while (true) {// 模拟线程在执行某些操作}});test1.setName("test1");test1.start();doSomething();}/** 保持程序一直执行 */private static void doSomething() {while (true) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}

运行结果:

在这里插入图片描述

2.2.3 阻塞状态(BLOCKED)

这个状态下的线程会暂停当前的执行。通常情况下,一个线程在等待锁(锁已被其他线程占用)的时候,这个线程会进入阻塞状态。

测试程序:

public class Test3 {public static void main(String[] args) throws InterruptedException {Thread test1 = new Thread(() -> {enter();});Thread test2 = new Thread(() -> {enter();});/*两个线程都尝试进入enter()方法,但只有一个线程能够拿到 `synchronized` 锁并进入方法,另外一个线程尝试进入方法但会被阻塞。*/test1.setName("test1");test2.setName("test2");test1.start();test2.start();}private synchronized static void enter() {System.out.printf("线程[%s]进来了,它将执行以下耗时的业务操作\n", Thread.currentThread().getName());for (long j = 0L; j < 50000000000L; j++) {}System.out.printf("线程[%s]退出了,释放了锁\n", Thread.currentThread().getName());}
}

运行结果:

# 控制台打印信息:
线程[test1]进来了,它将执行以下耗时的业务操作(以上是目前为止已打印的结果)

此时,线程test1处于运行状态,而线程test2因为没有拿到锁(锁被test1占用),所以处在阻塞BLOCKED状态。

在这里插入图片描述

2.2.4 等待状态(WATING)

(1/2) 情况1:线程本身手动调用 wait() 方法

执行 wait() 方法的那个线程,在执行了该方法后,会释放锁,并进入等待状态WAITING(只有别的线程调用notifyAll()方法才能唤醒该线程)

测试程序:

public class Test3 {public static void main(String[] args) throws InterruptedException {Thread test1 = new Thread(() -> {enter();});Thread test2 = new Thread(() -> {enter();});test1.setName("test1");test2.setName("test2");test1.start();test2.start();}private synchronized static void enter() {System.out.printf("线程[%s]进来了,它将执行以下耗时的业务操作\n", Thread.currentThread().getName());for (long j = 0L; j < 100000000000L; j++) {if (j == 30000000000L) {System.out.printf("线程[%s]执行到一半时,调用了wait()方法\n", Thread.currentThread().getName());try {// 冷知识1:wait()方法必须通过同步监视器对象(monitor)来调用// 冷知识2:静态的同步方法中,monitor默认是当前类的class对象,即Test3.class// 所以这里必须通过"Test3.class"来调用wait()方法,否则会报错Test3.class.wait();} catch (InterruptedException e) {e.printStackTrace();}}}System.out.printf("线程[%s]执行完毕\n", Thread.currentThread().getName());}
}

运行结果:

# 控制台打印信息:
线程[test1]进来了,它将执行以下耗时的业务操作
线程[test1]执行到一半时,调用了wait()方法
线程[test2]进来了,它将执行以下耗时的业务操作  # 此处说明了wait()方法会释放锁(以上是目前为止已打印的结果)

此时,第一个线程(test1)在方法中执行了wait()后,进入了WAITING状态。

在这里插入图片描述

(2/2) 情况2:某线程调用其他线程的join()方法

测试程序:

public class Test3 {private static Thread test1;private static Thread test2;public static void main(String[] args) throws InterruptedException {test1 = new Thread(() -> {System.out.println("线程test1开始执行");for (long j = 0L; j < 100000000000L; j++) {if (j == 30000000000L) {System.out.println("线程test1执行到一半时,调用了t2的join()方法");try {test2.join();} catch (InterruptedException e) {e.printStackTrace();}}}System.out.println("线程test1执行完毕");});test2 = new Thread(() -> {System.out.println("线程test2开始执行");for (long j = 0L; j < 100000000000L; j++) {}System.out.println("线程test2执行完毕");});test1.setName("test1");test2.setName("test2");test1.start();test2.start();}
}

运行结果:

# 控制台打印信息:
线程test1开始执行
线程test2开始执行
线程test1执行到一半时,调用了test2的join()方法 (以上是目前为止已打印的结果)

由于线程test1调用了test2.join(),因此线程test1会暂停执行,只有test2执行完毕后才被唤醒。
此时test1处在等待状态WAITING

在这里插入图片描述

2.2.5 计时等待状态(TIMED_WAITING)

(1/3) 情况1:线程本身手动调用 wait(time) 方法

和 2.2.4 等待状态(WAITING)情况1 其实是类似的,只是调用的方法由wait()变成了wait(time)

测试程序:

public class Test3 {public static void main(String[] args) throws InterruptedException {Thread test1 = new Thread(() -> {enter();});Thread test2 = new Thread(() -> {enter();});test1.setName("test1");test2.setName("test2");test1.start();test2.start();}private synchronized static void enter() {System.out.printf("线程[%s]进来了,它将执行以下耗时的业务操作\n", Thread.currentThread().getName());for (long j = 0L; j < 100000000000L; j++) {if (j == 30000000000L) {System.out.printf("线程[%s]执行到一半时,调用了wait(time)方法\n", Thread.currentThread().getName());try {// wait()方法必须通过同步监视器(monitor)来调用// 冷知识:静态的同步方法中,monitor默认是当前类的class对象,即Test3.class// 所以这里必须通过"Test3.class"来调用wait()方法,否则会报错Test3.class.wait(10 * 1000);} catch (InterruptedException e) {e.printStackTrace();}}}System.out.printf("线程[%s]执行完毕\n", Thread.currentThread().getName());}
}

运行结果:

# 控制台打印信息:
线程[test1]进来了,它将执行以下耗时的业务操作
线程[test1]执行到一半时,调用了wait(time)方法
线程[test2]进来了,它将执行以下耗时的业务操作(以上是目前为止已打印的结果)

此时test1线程进入了计时等待状态(TIMED_WAITING

在这里插入图片描述

(2/3) 情况2:某线程调用其他线程的join(time)方法

和 2.2.4 等待状态(WAITING)情况2 其实是类似的,只是调用的方法由join()变成了join(time)

测试程序:

public class Test3 {private static Thread test1;private static Thread test2;public static void main(String[] args) throws InterruptedException {test1 = new Thread(() -> {System.out.println("线程test1开始执行");for (long j = 0L; j < 100000000000L; j++) {if (j == 30000000000L) {System.out.println("线程test1执行到一半时,调用了test2的join(time)方法");try {test2.join(10 * 1000);} catch (InterruptedException e) {e.printStackTrace();}}}System.out.println("线程test1执行完毕");});test2 = new Thread(() -> {System.out.println("线程test2开始执行");for (long j = 0L; j < 100000000000L; j++) {}System.out.println("线程test2执行完毕");});test1.setName("test1");test2.setName("test2");test1.start();test2.start();}
}

运行结果:

# 控制台打印信息:
线程test1开始执行
线程test2开始执行
线程test1执行到一半时,调用了test2的join(time)方法 (以上是目前为止已打印的结果)

由于线程test1调用了test2.join(time)test1会处在计时等待状态TIMED_WAITING

在这里插入图片描述

(3/3) 情况3:某线程调用sleep(time)方法

线程调用Thread.sleep(time)方法后,也会进入计时等待状态(TIMED_WAITING

测试程序:

public class Test3 {public static void main(String[] args) throws InterruptedException {Thread test1 = new Thread(() -> {try {System.out.println("年轻人身体就是好,一进来工作时倒头就睡");Thread.sleep(100 * 1000);} catch (InterruptedException e) {e.printStackTrace();}});test1.setName("test1");test1.start();}
}

运行结果:

# 控制台打印信息:
年轻人身体就是好,一进来工作时倒头就睡

在这里插入图片描述

2.2.6 终止状态(TERMINATED)

当一个线程逻辑已执行完毕,或出现了异常而终止的时候,此时线程状态应该是 TERMINATED

这个状态下的线程用arthas工具获取不到,所以就只能用代码获取了。

测试程序:

public class Test3 {public static void main(String[] args) throws InterruptedException {Thread test1 = new Thread(() -> {System.out.println("test1执行过程出现异常");int a = 1 / 0;});Thread test2 = new Thread(() -> {for (int i = 0; i < 100; i++) {int a = 666;}System.out.println("test2任务已执行完毕");});test1.setName("test1");test2.setName("test2");test1.start();test2.start();Thread.sleep(1000);System.out.printf("线程[%s]的状态: %s\n", test1.getName(), test1.getState());System.out.printf("线程[%s]的状态: %s\n", test2.getName(), test2.getState());}
}

运行结果:

# 控制台打印信息:
test1执行过程出现异常
test2任务已执行完毕
Exception in thread "test1" java.lang.ArithmeticException: / by zeroat draft.Test3.lambda$main$0(Test3.java:8)at java.lang.Thread.run(Thread.java:748)
线程[test1]的状态: TERMINATED
线程[test2]的状态: TERMINATED

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

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

相关文章

ELK高级搜索(一)

文章目录 ELK搜索1&#xff0e;简介1.1 内容1.2 面向 2&#xff0e;Elastic Stack2.1 简介2.2 特色2.3 组件介绍 3&#xff0e;Elasticsearch3.1 搜索是什么3.2 数据库搜索3.3 全文检索3.4 倒排索引3.5 Lucene3.6 Elasticsearch3.6.1 Elasticsearch的功能3.6.2 Elasticsearch使…

【uniapp】样式合集

1、修改uni-data-checkbox多选框的样式为单选框的样式 我原先是用的单选&#xff0c;但是单选并不支持选中后&#xff0c;再次点击取消选中&#xff1b;所以我改成了多选&#xff0c;然后改变多选样式&#xff0c;让他看起来像单选 在所在使用的页面上修改样式即可 <uni-d…

Android Gradle 骚操作,将两个项目合并到一个项目中

1. 前言 在工作中&#xff0c;由于各种原因&#xff0c;导致需要将两个可单独运行的App项目&#xff0c;合并到一个git仓库里&#xff0c;且单独的App项目里还有其他Module模块。 如果只是将两个项目复制到同一个文件夹下&#xff0c;还是得单独打开各个项目&#xff0c;是很不…

CCL 2023 电信网络诈骗案件分类评测-第一名方案

1 任务内容 1.1 任务背景 2022年12月1日起&#xff0c;新出台的《反电信网络诈骗犯罪法》正式施行&#xff0c;表明了我国治理当前电信网络诈骗乱象的决心。诈骗案件分类问题是打击电信网路诈骗犯罪过程中的关键一环&#xff0c;根据不同的诈骗方式、手法等将其分类&#xff…

Leetcode每日一题:141. 环形链表、142. 环形链表 II、143. 重排链表(2023.7.29、30、31 C++)

目录 141. 环形链表 问题描述&#xff1a; 实现代码与解析&#xff1a; 快慢指针&#xff1a; 原理思路&#xff1a; 142. 环形链表 II 问题描述&#xff1a; 实现代码与解析&#xff1a; 快慢指针 原理思路&#xff1a; 143. 重排链表 题目描述&#xff1a; 实现…

分布式系统的 38 个知识点

天天说分布式分布式&#xff0c;那么我们是否知道什么是分布式&#xff0c;分布式会遇到什么问题&#xff0c;有哪些理论支撑&#xff0c;有哪些经典的应对方案&#xff0c;业界是如何设计并保证分布式系统的高可用呢&#xff1f; 1. 架构设计 这一节将从一些经典的开源系统架…

基于ASP.NET MVC开发的、开源的个人博客系统

推荐一个功能丰富、易于使用和扩展的开源博客&#xff0c;可以轻松地创建和管理自己的博客。 项目简介 基于.Net Framework 4.5开发的、开源博客系统&#xff0c;具有丰富的功能&#xff0c;包括文章发布、分类、标签、评论、订阅、统计等功能&#xff0c;同时也可以根据需要…

Stable Diffusion如何生成高质量的图-prompt写法介绍

文章目录 Stable Diffusion使用尝试下效果prompt的编写技巧prompt 和 negative promptPrompt格式Prompt规则细节优化Guidance Scale 总结 Stable Diffusion Stable Diffusion是一个开源的图像生成AI系统,由Anthropic公司开发。它基于 Transformer模型架构,可以通过文字描述生成…

TypeScript 【type】关键字的进阶使用方式

导语&#xff1a; 在前面章节中&#xff0c;我们了解到 TS 中 type 这个关键字&#xff0c;常常被用作于&#xff0c;定义 类型别名&#xff0c;用来简化或复用复杂联合类型的时候使用。同时也了解到 为对象定义约束接口类型 的时候所使用的是 Interfaces。 其实对于前面&#…

安装金蝶云星空出错 T_META FORMENUMITEM

找不到对象"T_META FORMENUMITEM”&#xff0c;因为它不存在或者你没有所需的权限 解决方案 如果出现以下问题

Tomcat虚拟主机

Tomcat虚拟主机 部署 [rootlocalhost webapps]# cd ../conf [rootlocalhost conf]# pwd /usr/local/tomcat/conf [rootlocalhost conf]# vim server.xml #增加虚拟主机配置&#xff0c;添加以下&#xff1a; <Host name"www.a.com" appBase"webapps"u…

MySQL和Oracle区别

由于SQL Server不常用&#xff0c;所以这里只针对MySQL数据库和Oracle数据库的区别 (1) 对事务的提交 MySQL默认是自动提交&#xff0c;而Oracle默认不自动提交&#xff0c;需要用户手动提交&#xff0c;需要在写commit;指令或者点击commit按钮 (2) 分页查询 MySQL是直接在SQL…

OpenHarmony开源鸿蒙学习入门 - 基于3.2Release 应用开发环境安装

OpenHarmony开源鸿蒙学习入门 - 基于3.2Release 应用开发环境安装 基于目前官方master主支&#xff0c;最新文档版本3.2Release&#xff0c;更新应用开发环境安装文档。 一、安装IDE&#xff1a; 1.IDE安装的系统要求 2.IDE下载官网链接&#xff08;IDE下载链接&#xff09; …

无人机自动返航的关键技术有哪些

无人机的广泛应用使得无人机自动返航技术变得至关重要。在各种应对意外情况的背景下&#xff0c;无人机自动返航技术的发展对确保无人机的安全&#xff0c;以及提高其应用范围具有重要意义。接下来&#xff0c;便为大家详细介绍无人机自动返航所运用到的关键技术。 一、定位与导…

ChatGPT辅助写论文:提升效率与创造力的利器

写作是人类最重要的交流方式之一&#xff0c;也是学术研究中不可或缺的环节。然而&#xff0c;写作并不是一件容易的事情&#xff0c;尤其是对于科研人员来说&#xff0c;他们需要花费大量的时间和精力来撰写高质量的论文&#xff0c;并且面临着各种各样的挑战&#xff0c;如语…

前端Web实战:从零打造一个类Visio的流程图拓扑图绘图工具

前言 大家好&#xff0c;本系列从Web前端实战的角度&#xff0c;给大家分享介绍如何从零打造一个自己专属的绘图工具&#xff0c;实现流程图、拓扑图、脑图等类Visio的绘图工具。 你将收获 免费好用、专属自己的绘图工具前端项目实战学习如何从0搭建一个前端项目等基础框架项…

2023年电赛---运动目标控制与自动追踪系统(E题)关于网友的问题回复

前言 &#xff08;1&#xff09;各位私信问问题之前&#xff0c;看看自己的问题是不是在这个里面再问&#xff01; &#xff08;2&#xff09; <1>2023年8月3日&#xff0c;10点25分。增加第三问的细节回答。 <1>2023年8月3日&#xff0c;10点45分。更新关于舵机安…

掌握Python的X篇_16_list的切片、len和in操作

接上篇掌握Python的X篇_15_list容器的基本使用&#xff0c;本篇进行进一步的介绍。 文章目录 1. list的索引下标可以是负数2. 切片&#xff08;slice&#xff09;2.1 切片基础知识2.2 如何“取到尽头”2.3 按照步长取元素2.4 逆序取值 3. len函数获取lis的元素个数4. in操作符…

时间复杂度接近O(n)的三种排序算法

1.桶排序 桶排序&#xff0c;顾名思义&#xff0c;会用到“桶”&#xff0c;核心思想是将要排序的数据分到几个有 序的桶里&#xff0c;每个桶内的数据再单独进行排序。桶内排完序之后&#xff0c;再把每个桶内的数据按照顺序依次 取出&#xff0c;组成的序列就是有序的了。 …

C语言-------函数栈帧的创建和销毁------剖析描骨

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; &#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382;…