面试题:说说Java线程的状态及转换

文章目录

  • 为何要了解Java线程状态
  • Java线程状态转换图
  • Java线程有哪些状态?
  • 关于wait()放在while循环的疑问
  • BLOCKED 和 WAITING 状态的区别和联系


为何要了解Java线程状态

线程是 JVM 执行任务的最小单元,理解线程的状态转换是理解后续多线程问题的基础。

Java线程状态转换图

在这里插入图片描述

Java线程有哪些状态?

在 JVM 运行中,线程一共有 NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED 六种状态,这些状态对应 Thread.State 枚举类中的状态。

Thread.State枚举源码:

为方便阅读,在此去掉了文档注释

public enum State {NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED;
}

在给定的时间点,线程只能处于这些状态中的一种状态。这些状态是不反映任何操作系统线程状态的虚拟机状态。

NEW,TERMINATED

这两个状态比较好理解,当创建一个线程后,还没有调用start()方法时,线程处在 NEW 状态,线程完成执行,退出后变为TERMINATED终止状态。

RUNNABLE

运行 Thread 的 start 方法后,线程进入 RUNNABLE 可运行状态

/*** 程序目的:观察线程的各种状态*/
class MyThread extends Thread {@Overridepublic void run() {System.out.printf("%s线程运行\n", Thread.currentThread().getName());}
}/*** 分别观察创建线程后、start()后、和线程退出后的线程状态。* 其中Thread.sleep(50);是为了等待线程执行完*/
public class ThreadStateDemo {public static void main(String[] args) throws InterruptedException {MyThread myThread = new MyThread();System.out.printf("创建线程后,线程的状态为:%s\n", myThread.getState());myThread.start();System.out.printf("调用start()方法后线程的状态为:%s\n", myThread.getState());// 休眠50毫秒,等待MyThread线程执行完Thread.sleep(50);System.out.printf("再次打印线程的状态为:%s\n", myThread.getState());}
}

输出结果:

创建线程后,线程的状态为:NEW
调用start()方法后线程的状态为:RUNNABLE
Thread-0线程运行
再次打印线程的状态为:TERMINATED

我们可以看到,输出结果符合预期。

  • 在刚创建完线程后,状态为NEW
  • 调用了start()方法后线程的状态变为:RUNNABLE。
  • 然后,我们看到了run()方法的执行,这个执行,是在主线程main打印了调用start()方法后线程的状态为:RUNNABLE输出后执行的。
  • 随后,我们让main线程休眠了50毫秒,等待MyThread线程退出
  • 最后再打印MyThread线程的状态,为TERMINATED。

BLOCKED

如图左侧所示,在运行态中的线程进入 synchronized 同步块或者同步方法时,如果获取锁失败,则会进入到 BLOCKED 状态。当获取到锁后,会从 BLOCKED 状态恢复到就绪状态。

import lombok.extern.slf4j.Slf4j;/*** 程序目的:观察线程的BLOCKED状态*/
@Slf4j
public class ThreadBlockedStateDemo {public static void main(String[] args) {Thread threadA = new Thread(() -> method01(), "A-Thread");Thread threadB = new Thread(() -> method01(), "B-Thread");threadA.start();threadB.start();log.info("线程A的状态为:{}", threadA.getState());log.info("线程B的状态为:{}", threadB.getState());}/*** 停顿10毫秒、模拟方法执行耗时*/public static synchronized void method01() {log.info("[{}]:开始执行主线程的方法", Thread.currentThread().getName());try {Thread.sleep(10);}catch (InterruptedException e) {e.printStackTrace();}log.info("[{}]:主线程的方法执行完毕", Thread.currentThread().getName());}
}

输出结果:

2020-06-26 20:32:15.404 [A-Thread] INFO  com.hua.threadtest.state.ThreadBlockedStateDemo - [A-Thread]:开始执行主线程的方法
2020-06-26 20:32:15.404 [main    ] INFO  com.hua.threadtest.state.ThreadBlockedStateDemo - 线程A的状态为:RUNNABLE
2020-06-26 20:32:15.407 [main    ] INFO  com.hua.threadtest.state.ThreadBlockedStateDemo - 线程B的状态为:BLOCKED
2020-06-26 20:32:15.417 [A-Thread] INFO  com.hua.threadtest.state.ThreadBlockedStateDemo - [A-Thread]:主线程的方法执行完毕
2020-06-26 20:32:15.418 [B-Thread] INFO  com.hua.threadtest.state.ThreadBlockedStateDemo - [B-Thread]:开始执行主线程的方法
2020-06-26 20:32:15.430 [B-Thread] INFO  com.hua.threadtest.state.ThreadBlockedStateDemo - [B-Thread]:主线程的方法执行完毕

A线程优先获得到了锁,状态为RUNNABLE,这时,B线程处于BLOCKED状态。

当A线程执行完毕后,B线程执行对应方法。

WAITING,TIMED_WAITING

如图右侧所示,运行中的线程还会进入等待状态,这两个等待一个是有超时时间的等待,例如调用 Object.wait、Thread.join 等;另外一个是无超时的等待,例如调用 Thread.join 或者 Locksupport.park等。这两种等待都可以通过 notify 或 unpark 结束等待状态并恢复到就绪状态。

官方文档说明为:

A thread in the waiting state is waiting for another thread to perform a particular action. For example, a thread that has called Object.wait() on an object is waiting for another thread to call Object.notify() or Object.notifyAll() on that object. A thread that has called Thread.join() is waiting for a specified thread to terminate.

处于等待状态的线程正在等待另一个线程执行特定的操作。

接下来我们来模拟一下线程的WAITING状态:

import lombok.extern.slf4j.Slf4j;/*** <pre>* 程序目的:观察线程的WAITING状态* 模拟:只有一个售票窗口的售票厅,有两个粉丝都想买票。* 如果没有票,他们就继续等待、如果有票,则买票、然后离开售票厅。* 其中,工作人员会补票,补票之后,粉丝就可以买到票了。* </pre>*/
@Slf4j
public class ThreadWaitingStateDemo {public static void main(String[] args) throws InterruptedException {Ticket ticket = new Ticket();Thread threadA = new Thread(() -> {synchronized (ticket) {while (ticket.getNum() == 0) {try {ticket.wait();}catch (InterruptedException e) {e.printStackTrace();}}ticket.buy();}}, "粉丝A");Thread threadB = new Thread(() -> {synchronized (ticket) {while (ticket.getNum() == 0) {try {ticket.wait();}catch (InterruptedException e) {e.printStackTrace();}}ticket.buy();}}, "粉丝B");threadA.start();threadB.start();// 确保A和B线程都运行起来Thread.sleep(10);log.info("粉丝A线程的状态为:{}", threadA.getState());log.info("粉丝B线程的状态为:{}", threadB.getState());Thread employeeThread = new Thread(() -> {synchronized (ticket) {if (ticket.getNum() == 0) {ticket.addTickt();ticket.notifyAll();}}}, "补票员");employeeThread.start();}}@Slf4j
class Ticket {/*** 票的张数*/private int num = 0;public int getNum() {return num;}public void addTickt() {try {Thread.sleep(2_000);}catch (InterruptedException e) {e.printStackTrace();}log.info("补充票");this.num += 2;}/*** 停顿10毫秒、模拟方法执行耗时*/public void buy() {log.info("[{}]:购买了一张票", Thread.currentThread().getName());log.info("[{}]:退出售票厅", Thread.currentThread().getName());}
}

输出:

2020-06-26 21:26:37.938 [main    ] INFO  com.hua.threadtest.state.ThreadWaitingStateDemo - 粉丝A线程的状态为:WAITING
2020-06-26 21:26:37.945 [main    ] INFO  com.hua.threadtest.state.ThreadWaitingStateDemo - 粉丝B线程的状态为:WAITING
2020-06-26 21:26:39.948 [补票员     ] INFO  com.hua.threadtest.state.Ticket - 补充票
2020-06-26 21:26:39.949 [粉丝B     ] INFO  com.hua.threadtest.state.Ticket - [粉丝B]:购买了一张票
2020-06-26 21:26:39.949 [粉丝B     ] INFO  com.hua.threadtest.state.Ticket - [粉丝B]:退出售票厅
2020-06-26 21:26:39.949 [粉丝A     ] INFO  com.hua.threadtest.state.Ticket - [粉丝A]:购买了一张票
2020-06-26 21:26:39.949 [粉丝A     ] INFO  com.hua.threadtest.state.Ticket - [粉丝A]:退出售票厅

当修改ticket.wait();为ticket.wait(10);后,输出结果如下:

2020-06-26 21:27:10.704 [main    ] INFO  com.hua.threadtest.state.ThreadWaitingStateDemo - 粉丝A线程的状态为:TIMED_WAITING
2020-06-26 21:27:10.709 [main    ] INFO  com.hua.threadtest.state.ThreadWaitingStateDemo - 粉丝B线程的状态为:TIMED_WAITING
2020-06-26 21:27:12.714 [补票员     ] INFO  com.hua.threadtest.state.Ticket - 补充票
2020-06-26 21:27:12.714 [粉丝B     ] INFO  com.hua.threadtest.state.Ticket - [粉丝B]:购买了一张票
2020-06-26 21:27:12.714 [粉丝B     ] INFO  com.hua.threadtest.state.Ticket - [粉丝B]:退出售票厅
2020-06-26 21:27:12.715 [粉丝A     ] INFO  com.hua.threadtest.state.Ticket - [粉丝A]:购买了一张票
2020-06-26 21:27:12.715 [粉丝A     ] INFO  com.hua.threadtest.state.Ticket - [粉丝A]:退出售票厅

关于wait()放在while循环的疑问

为什么ticket.wait();要放在while (ticket.getNum() == 0)代码块中呢?既然这行代码时让线程等待着,那使用if不就行了?

我们设想一下,如果使用if,则在线程被唤醒后,会继续往下执行,不再判断条件是否符合,这时还是没有票,粉丝也就购买不到票了。

我们看一下Object.wait()的官方doc说明:

As in the one argument version, interrupts and spurious wakeups are possible, and this method should always be used in a loop:synchronized (obj) {while (<condition does not hold>)obj.wait();... // Perform action appropriate to condition}

在一个参数版本中(wait方法),中断和虚假的唤醒是可能的,这个方法应该总是在循环中使用。

我们再继续看Object.wait(long timeout)的文档说明:

A thread can also wake up without being notified, interrupted, or
timing out, a so-called spurious wakeup. While this will rarely occur
in practice, applications must guard against it by testing for the
condition that should have caused the thread to be awakened, and
continuing to wait if the condition is not satisfied. In other words,
waits should always occur in loops

线程也可以在没有通知、中断或超时的情况下被唤醒,这就是所谓的假唤醒。虽然这种情况在实践中很少发生,但应用程序必须通过测试导致线程被唤醒的条件来防止这种情况发生,如果条件不满足,则继续等待。换句话说,等待应该总是在循环中发生

所以,为了避免很少发生的假唤醒出现时程序发生不可预知的错误,建议把wait()调用放在循环语句中。这样就算被假唤醒,也有条件语句的限制。

这也是为何wait要放在循环语句中的一个原因。

BLOCKED 和 WAITING 状态的区别和联系

表:处于等待状态的各种细分状态对比

在这里插入图片描述

简单来说,处于BLOCKED状态的线程,还是在竞争锁的,一旦cpu有时间,它竞争到了锁、就会执行。

但是WAITING状态的线程则不去竞争锁,需要等待被动通知、或者自己定的闹钟(等待时间)到了、再去竞争锁。

一图胜千言,在此引用一张国外一位大牛画的图:

图:线程转换-详细

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

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

相关文章

网站为什么需要https证书以及如何申请

随着互联网的快速发展&#xff0c;网站的安全性问题越来越受到人们的关注。因此&#xff0c;越来越多的网站开始使用https证书&#xff0c;以保护用户的数据安全和隐私。那么&#xff0c;网站为什么需要https证书呢&#xff1f; 首先&#xff0c;https证书可以提供加密保护&…

ROS IMU 数据发布---rviz_imu_plugin的安装

ROS中发布IMU传感器消息 - 润新知 按照上述链接的方法执行 catkin_make install -DCMAKE_INSTALL_PREFIX/opt/ros/noetic 后报错 这个错误是因为在安装过程中&#xff0c;CMake无法将文件复制到目标路径。这可能是由于权限不足导致的。可以尝试使用以下命令更改目标文件夹的…

破解mariadb密码

破解mariadb密码 小白教程&#xff0c;一看就会&#xff0c;一做就成。 1.先停止mariadb systemctl stop mariadb.service 2.进单用户模式 mysqld_safe --skip-grant-tables & 3.登录mariadb mysql -uroot #&#xff08;不用密码也能登录&#xff09; 4.切换到mysql …

堆叠、集群技术

1.堆叠、集群技术的概述 堆叠、集群简介 堆叠&#xff08;iStack&#xff09;&#xff0c;将多台支持堆叠特性的交换机通过堆叠线缆连接在一起&#xff0c;从逻辑上虚拟成一台交换设备&#xff0c;作为一个整体参与数据转发。 集群&#xff08;Cluster Switch System&#xf…

Davinci 集成NvM协议栈的步骤

BSW添加NvM和MemIf模块 Mcal添加Fls、Fee和Crc模块 NvM中添加数据块&#xff0c;Fee中添加相应的数据块。Mcal如果使用EB生成&#xff0c;需要在EB中配置Fee&#xff0c;或Davinci中配置好之后把配置导入到EB中。 NvM和Fee模块配置中不要启用Polling。 Fee模块需要启用Eras…

解决uniapp里scroll-view横向滚动的问题

一、前言 本以为是一件很简单的事&#xff0c;结果浪费了整整一个上午&#xff0c;并且问题并没有全部解决....后来没办法&#xff0c;用了touchmove模拟的滑动&#xff0c;如果有好的解决方法麻烦告诉我...非常感谢~ 一、问题 其实我想要实现的功能很简单&#xff0c;就是一…

elasticsearch(ES)分布式搜索引擎04——(数据聚合,自动补全,数据同步,ES集群)

目录 1.数据聚合1.1.聚合的种类1.2.DSL实现聚合1.2.1.Bucket聚合语法1.2.2.聚合结果排序1.2.3.限定聚合范围1.2.4.Metric聚合语法1.2.5.小结 1.3.RestAPI实现聚合1.3.1.API语法1.3.2.业务需求1.3.3.业务实现 2.自动补全2.1.拼音分词器2.2.自定义分词器2.3.自动补全查询2.4.实现…

【iOS】Fastlane一键打包上传到TestFlight、蒲公英

Fastlane一键打包上传到TestFlight、蒲公英 前言一、准备二、探索一、Fastlane配置1、Fastlane安装2、Fastlane更新3、Fastlane卸载4、查看Fastlane版本5、查看Fastlane位置6、Fastlane初始化 二、Fastlane安装蒲公英插件三、Fastlane文件编辑1、Gemfile文件2、Appfile文件3、F…

【安全】 Java 过滤器 解决存储型xss攻击问题

文章目录 XSS简介什么是XSS?分类反射型存储型 XSS(cross site script)跨站脚本攻击攻击场景解决方案 XSS简介 跨站脚本( cross site script )为了避免与样式css(Cascading Style Sheets层叠样式表)混淆&#xff0c;所以简称为XSS。 XSS是一种经常出现在web应用中的计算机安全…

stm32学习笔记:EXIT中断

1、中断系统 中断系统是管理和执行中断的逻辑结构&#xff0c;外部中断是众多能产生中断的外设之一。 1.中断&#xff1a; 在主程序运行过程中&#xff0c;出现了特定的中断触发条件 (中断源&#xff0c;如对于外部中断来说可以是引脚发生了电平跳变&#xff0c;对于定时器来…

24v转12v电源芯片 24v转5v开关电源芯片AH7691

AH7691是一款高-效-率、高压降压型DC-DC转换器。该芯片固定在130KHz的开关频率下工作&#xff0c;能够提供3A的输出电流能力&#xff0c;并具有低纹波、***软启动功能、过压保护功能和温度保护。 AH7691还具备峰值限流功能&#xff0c;使电路设计更加简单化。该芯片内置集成了高…

JFLASH基本使用总结

注意&#xff0c;不同版本的操作略有不同&#xff0c;本教程以J-Flash V5.12f为例。 烧录文件 如果是刚打开J-Flash&#xff0c;会弹出这样的一个工程选择界面&#xff0c;可以选择已有工程&#xff0c;或者创建新的工程&#xff0c;我们这里选择创建新工程。 注意&#xff0…

ffmpeg从一个视频中提取音频

ffmpeg -i ~/video/video.mp4 -vn -acodec copy ~/video/audioFile.m4a 从video.mp4中提取音频到文件audioFile.m4a中 查看提取的音频文件 ffprobe ~/video/audioFile.m4a

技术先驱视角:长城汽车工程师揭秘Hi4技术的无限潜力

文 | 智能相对论 作者 | 沈浪 汽车行业的变革正在回归平衡和理性&#xff0c;混动市场再度掀起新的浪潮&#xff0c;以Hi4技术为代表的混合动力解决方案备受瞩目&#xff0c;并爆发出无限潜力。 日前&#xff0c;工信部等七个部门联合印发了《关于汽车行业稳增长工作方案&am…

AlGaN/GaN结构的氧基数字蚀刻

引言 宽带隙GaN基高电子迁移率晶体管(HEMTs)和场效应晶体管(fet)能够提供比传统Si基高功率器件更高的击穿电压和电子迁移率。常关GaN非常需要HEMT来降低功率并简化电路和系统架构&#xff0c;这是GaN HEMT技术的主要挑战之一。凹进的AlGaN/GaN结构是实现常关操作的有用选择之一…

Xception:使用Tensorflow从头开始实现

一、说明 近年来&#xff0c;卷积神经网络已成为计算机视觉领域的主要算法&#xff0c;开发设计它们的方法一直是相当的关注。Inception模型似乎能够用更少的参数学习更丰富的表示。它们是如何工作的&#xff0c;以及它们与常规卷积有何不同&#xff1f;本文将用tensorflow实现…

基本微信小程序的购物商城系统

项目介绍 随着互联网的趋势的到来&#xff0c;各行各业都在考虑利用互联网将自己的信息推广出去&#xff0c;最好方式就是建立自己的平台信息&#xff0c;并对其进行管理&#xff0c;随着现在智能手机的普及&#xff0c;人们对于智能手机里面的应用购物平台小程序也在不断的使…

谷歌浏览器 ERR_MANDATORY_PROXY_CONFIGURATION_FAILED 报错的处理方式

今天早上到公司 还是和往常一样 电脑开机 打开谷歌浏览器 搜索资料结果给我报错了 2.原先我的谷歌浏览器配置的搜索引擎为百度 3.电脑上面的火狐 ie浏览器都是可以的排除电脑网络方面的问题(dns也有解析谷歌路径的这个有兴趣的可以研究) 最终还是刷新了电脑的dns ,清除了谷歌…

嵌入式Linux裸机开发(六)EPIT 定时器

系列文章目录 文章目录 系列文章目录前言介绍配置过程前言 前面学的快崩溃了,这也太底层了,感觉学好至少得坚持一整年,我决定这节先把EPIT学了,下面把常见三种通信大概学一下,直接跳过其他的先学移植了,有些太多了内容。 介绍 EPIT(Enhanced Periodic Interrupt Timer…

CSS 中::after的妙用(实现在margin中显示内容)

效果图如下&#xff1a; 背景&#xff1a; 如上图&#xff0c;之前只是当纯的写一个参考货架平面图&#xff0c;用作物料系统的在库状态可视化&#xff0c;当完成页面body分成10等份时&#xff0c;货架之间需要有通道&#xff0c;为了实现实际的样式&#xff0c;我给每个等份都…