多线程·线程状态


目录

 1.等待一个线程 join

2.休眠当前线程

3.线程的所有状态

4.线程的状态转换 


 1.等待一个线程 join

 有些场景,我们需要控制线程的执行顺序,这时候就需要用到 join

比如:把大象装进冰箱要几步?

第一步:打开冰箱;

第二步:把大象装进去;

第三步:把冰箱关上。

把每一步都当成一个线程来看,那么线程2就要等线程1完成、线程3就等线程2完成。

public class Main {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{System.out.println("打开冰箱");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}});Thread t2 = new Thread(()->{System.out.println("把大象装进去");try{Thread.sleep(1000);}catch (InterruptedException e){e.printStackTrace();}});Thread t3 = new Thread(()->{System.out.println("把冰箱关上");try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}});t1.start();t1.join();//main线程等待线程t1完成System.out.println("冰箱已经打开了");t2.start();t2.join();//main线程等待线程t2完成System.out.println("大象已经装进去了");t3.start();t3.join();//确保线程t3完成System.out.println("冰箱关上了,任务完成!");}
}

这里 start() 之后,t1main 线程就开始并发执行了,当 main 线程执行到 t1.join() 表示 main 线程得等 t1 线程执行完毕后,才能接着往后执行代码!只要 t1 线程没有执行完毕,main 线程就会发生阻塞,一直阻塞到 t1 线程执行完毕,也就是执行完对应的 run 方法!

像上述代码是可以等到 t1、t2、t3线程执行完毕了,如果 t 线程执行的任务里面出现了死循环,此时 main 线程不就无休止的等待了?确实如此,但是 join 给我们提供了一个带参数的方法,等指定的时间,到点就不等了! 如下代码:

public class Main {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{System.out.println("打开冰箱");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}});Thread t2 = new Thread(()->{System.out.println("把大象装进去");try{Thread.sleep(1000);}catch (InterruptedException e){e.printStackTrace();}});Thread t3 = new Thread(()->{System.out.println("把冰箱关上");try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}});t1.start();t1.join(10000);//main线程等待线程t1完成System.out.println("冰箱已经打开了");t2.start();t2.join(10000);//main线程等待线程t2完成System.out.println("大象已经装进去了");t3.start();t3.join(10000);//确保线程t3完成System.out.println("冰箱关上了,任务完成!");}
}

把大象装进冰箱的三个步骤,若是main线程等了10秒钟还没有完成该任务,那么main线程就不等了,10秒后冰箱门还不开就执行下一步了···· 

还有一种情况:

打开冰箱还需要10秒?t1说:我1秒就可以把冰箱打开了!然后 main 线程执行 t1.join() 的时候,但是 t1 线程的 run 方法已经执行完了,此时 join 就不会阻塞了,就会立即返回!

2.休眠当前线程

休眠线程其实我们前面已经用了,其本质就是让这个线程不参与调度了(不去 CPU 上执行)。

通过调用 Thread 类 中的 sleep 静态方法,传入指定时间,就能令线程休眠了。

注意:哪个线程里调用 Thread.sleep(1000),就让哪个线程休眠 1000 毫秒!

我们前面所说的 join 等待一个线程,当 main线程 调用到 t.join() 时候,main线程就要等待 t 线程 结束,此时main线程就进入了阻塞状态

而现在所讲的sleep休眠当前线程,也是进入阻塞状态!

那么如何进入阻塞状态呢? 

 在操作系统中有两个用于调度进程的重要队列:阻塞队列就绪队列, 当线程 t1 调用 sleep() 方法,就会进入阻塞队列,当 sleep 结束,就会进入就绪队列。

阻塞队列里的线程,也就是暂时不参与 CPU 的调度了,就绪队列中的线程,随时可以被 CPU 调度!

3.线程的所有状态

线程的状态是一个枚举类型Thread.State,我们可以打印出来看看都有哪些状态

public class Main {public static void main(String[] args) {for (Thread.State state : Thread.State.values()) {System.out.println(state);}}
}

  • NEW:                       安排了工作,还未开始行动,还没有调用 start 方法
  • RUNNABLE:           可工作的,又可以分成正在工作中即将开始工作
  • BLOCKED:             等待锁时产生的状态(锁竞争引起的阻塞)
  • WAITING:               调用 wait join 时进入的状态(死等引起的阻塞),
  • TIMED_WAITING:  调用 sleep 进入的状态
  • TERMINATED:       工作完成了

红标的状态都是阻塞状态,只是引起阻塞的原因不同,后面还会进行讲解

4.线程的状态转换 

线程从创建到销毁,期间会有一系列的状态变化,如下图: 

下面,我们通过代码来给大家演示一下线程的状态转换

public class Main {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {for (int i = 0; i < 10_0000; i++) {}});System.out.println("start之前: " + t.getState());t.start();System.out.println("线程执行中的状态 : " + t.getState());Thread.sleep(1000);System.out.println("线程结束后的状态 : " + t.getState());}
}

当线程进入TERMINATED状态时,也就是线程结束后,线程所对应的PCB销毁了,这时就无法重新启动线程,重启就会抛出异常,但是线程所对应的对象t还没有被回收,我们仍然可以借助对象t来调用方法和属性,只是无法通过多线程来干活了!

接下来通过代码给大家演示一下线程 sleep 时候的 TIMED_WAITING 状态

public class Main {public static void main(String[] args) {final Object object = new Object();Thread t = new Thread(new Runnable() {@Overridepublic void run() {synchronized(object) {while(true) {try{Thread.sleep(1000);}catch (InterruptedException e) {e.printStackTrace();}}}}});t.start();}

上述代码用到了 synchronized 这里暂时不作解释,将会在后续博客中进行讲解,我们可以看到,当我们的线程在循环执行 sleep ,此时我们就可以通过 jconsole 工具来查看线程的状态,该工具是jdk自带的,可以在bin目录中找到,有关于这个工具的讲解请看博主的上一篇博客 Thread类 的讲解,如上图所示,线程 sleep 后,就进入了TIMED_WAITING的状态,这个状态是线程在等待唤醒,等到一定时间没醒就自动醒了!

此时,我们可以修改一下上述的代码,把 t 中的 sleep 换成 wait ,我们来看看线程状态的变化

public class Main {public static void main(String[] args) {final Object object = new Object();Thread t = new Thread(new Runnable() {@Overridepublic void run() {synchronized(object) {while(true) {try{object.wait();
//                            Thread.sleep(1000);}catch (InterruptedException e) {e.printStackTrace();}}}}});t.start();}

我们通过 jconsole工具看看此时线程的状态 

 如上图所示,我们可以看到此时线程的状态就是WAITNG状态了,WAITING状态是线程在无限等待唤醒,若是没有唤醒这个线程,那么这个线程就会一直等下去,也就是我们所说的“死等

 由于BLOCKED状态涉及到了锁竞争,这里暂时不作讲解,大家了解一下即可,这个状态是两个线程在争夺同一把锁引起的状态,在后续博客中还会进行讲解

感谢观看,希望对您有所帮助! 

下期预告:线程安全

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

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

相关文章

Java基础面试题(5.14)

1.Java语言的特点&#xff1f; 1.一面向对象&#xff08;封装&#xff0c;继承&#xff0c;多态&#xff09;&#xff1b;2.平台无关性&#xff08; Java 虚拟机实现平台无关性&#xff09;&#xff1b;(类是一种定义对象的蓝图或模板)3.支持多线程&#xff08; C 语言没有内置…

React 学习-4

1.React 事件处理-传入函数作为事件处理函数 <button onClick{activateLasers}>激活按钮 </button> 注意事项&#xff1a;&#xff08;1&#xff09;阻止默认行为必须使用preventDefault,不能使用return false &#xff08;2&#xff09;ES6 class 语法来定义一个…

设计模式-13 - Prototype Design Pattern 原型设计模式

设计模式-13 - Prototype Design Pattern 原型设计模式 1.定义 原型设计模式是一种创建对象的方式&#xff0c;它通过复制一个现有的对象&#xff08;原型&#xff09;来创建一个新对象。 2.内涵 优点&#xff1a; 创建对象的高效方式&#xff1a;克隆一个对象比从头开始创建…

【数据结构陈越版笔记】第1章 概论

我最近准备以陈姥姥的数据结构教材为蓝本重新学一下数据结构&#xff0c;写一下读书笔记 第1章 概论 1.1 引子 概论中首先描述了&#xff0c;数据结构的定义没有具体的定义&#xff0c;初学者可以不用管这个定义的问题&#xff0c;但是我理解的和维基百科的说法是一样的“数…

全面了解 Swagger 导出功能的使用方式

Swagger 是一个强大的平台&#xff0c;专门用于开发、构建和记录 RESTful Web 接口。通过其提供的交互式用户界面&#xff0c;开发人员能够轻松且迅速地创建和测试 API。Swagger 还允许用户以多种格式&#xff0c;包括 JSON 和 Markdown&#xff0c;导出 API 文档。选择 JSON 格…

人工神经网络(科普)

人工神经网络&#xff08;Artificial Neural Network&#xff0c;即ANN &#xff09;&#xff0c;是20世纪80 年代以来人工智能领域兴起的研究热点。它从信息处理角度对人脑神经元网络进行抽象&#xff0c; 建立某种简单模型&#xff0c;按不同的连接方式组成不同的网络。在工程…

Android - 3段式耳机和4段式耳机

在看调整音频参数的相关文档时发现&#xff0c;audio模式下音频参数上还会对耳机有区分。  Headset4P&#xff1a; 4 段式耳机&#xff08; 8k LTENB &#xff09;  Headset3P&#xff1a; 3 段式耳机&#xff08; 8k LTENB &#xff09; 但不太清楚相关设计&#xff0…

MySQL中的索引失效问题

索引失效的情况 这是正常查询情况&#xff0c;满足最左前缀&#xff0c;先查有先度高的索引。 1. 注意这里最后一种情况&#xff0c;这里和上面只查询 name 小米科技 的命中情况一样。说明索引部分丢失&#xff01; 2. 这里第二条sql中的&#xff0c;status > 1 就是范围查…

error和exception的区别?

Error类: 一般是指与虚拟机相关的问题&#xff0c;如:系统崩溃,虚拟机错误&#xff0c;内存空间不足&#xff0c;方法调用栈溢出等。这类错误将会导致应用程序中断&#xff0c;仅靠程序本身无法恢复和预防; Exception 类:分为运行时异常和受检查的异常。 运行时异常:【如空指针…

什么品牌洗地机最好?怎么选?2024家用洗地机推荐攻略

随着科技的不断发展&#xff0c;家用洗地机已经成为人们家庭清洁任务重非常重要的辅助工具。家用洗地机集吸尘、扫地、拖地等功能于一体&#xff0c;通过高速旋转的滚刷和强力的吸力&#xff0c;将地面上的污渍、细菌和毛发等吸入污水箱&#xff0c;从而达到清洁地面的目的。但…

Uboot(三)

Uboot的移植 移植 U-Boot 到新的硬件平台通常涉及以下几个步骤&#xff1a; 了解目标硬件平台&#xff1a;首先&#xff0c;你需要详细了解目标硬件平台的架构、处理器类型、外设配置、存储器布局等信息。这包括查阅硬件手册、芯片手册、电路图以及原始的引导代码等。 获取 U…

Java设计模式-命令模式(16)

命令设计模式(Command Pattern)在Java中的实现细节如下所述,这将是一个详细的教程,涵盖模式的基本概念、组成部分、实现步骤、以及如何在实际开发中应用这一模式。 命令设计模式基础 命令模式是一种行为设计模式,它将请求封装成对象,允许你参数化客户对请求的调用,队列…

CentOS 磁盘挂载

查看磁盘挂载情况 df -hFilesystem Size Used Avail Use% Mounted on devtmpfs 3.9G 0 3.9G 0% /dev tmpfs 3.9G 0 3.9G 0% /dev/shm tmpfs 3.9G 17M 3.9G 1% /run tmpfs 3.9G 0 3.9G 0% /sys/fs/cgrou…

java static 关键字

在Java中&#xff0c;static是一个关键字&#xff0c;用于创建类级别的成员&#xff08;字段、方法、块&#xff09;。static成员属于类本身&#xff0c;而不是类的实例&#xff0c;因此可以直接通过类名访问&#xff0c;而不需要创建类的实例。 1. 静态字段&#xff08;Stati…

mysql查询某个字段重复数据

要查询MySQL中某个字段的重复数据&#xff0c;可以使用GROUP BY和HAVING子句。以下是一个示例SQL查询&#xff0c;它将找出table_name表中column_name字段的所有重复值及其出现的次数。 SELECT column_name, COUNT(*) FROM table_name GROUP BY column_name HAVING COUNT(*) &…

软件验收测试包括哪些类型

在软件开发过程中&#xff0c;验收测试是一个至关重要的环节&#xff0c;它确保了软件的质量、功能性和用户体验符合预期。验收测试主要关注于软件是否满足用户需求和业务目标&#xff0c;从而确保软件能够顺利交付并投入使用。本文将介绍软件验收测试的主要类型及其关键要素。…

扩展van Emde Boas树以支持卫星数据:设计与实现

扩展van Emde Boas树以支持卫星数据&#xff1a;设计与实现 1. 引言2. vEB树的基本概念3. 支持卫星数据的vEB树设计3.1 数据结构的扩展3.2 操作的修改3.3 卫星数据的存储和检索 4. 详细设计和实现4.1 定义卫星数据结构体4.2 修改vEB树节点结构4.3 插入操作的伪代码4.4 C语言实现…

最小化测试步骤

打镜像并导入公有云 开机 注意hosts文件格式,需要重新改正 3、出包测试 前置,开启yum缓存功能 vim /etc/yum.conf keepcache=1 installonlypkgs=* 正常部署 必要包yum -y install tar \ gzip \ telnet \ wget \ vim \ traceroute \ curl \ tcpdump \ createrepo \ sysst…

声纹识别的对抗与防御

随着机器学习理论和方法的发展, 出现了用于模仿特定说话人语音的深度伪造、针对语音识别和声纹识别的对抗样本, 它们都为破坏语音载体的可信性和安全性提供了具体手段, 进而对各自应用场景的信息安全构成了挑战。 深度伪造是利用生成式对抗网络等方法, 通过构建特定的模型, 产生…

【爬虫之scrapy框架——尚硅谷(学习笔记one)--基本步骤和原理+爬取当当网(基本步骤)】

爬虫之scrapy框架——基本原理和步骤爬取当当网&#xff08;基本步骤&#xff09; 下载scrapy框架创建项目&#xff08;项目文件夹不能使用数字开头&#xff0c;不能包含汉字&#xff09;创建爬虫文件&#xff08;1&#xff09;第一步&#xff1a;先进入到spiders文件中&#x…