简介

Java线程有6种状态,分别是NEWRUNNABLEBLOCKEDWAITINGTIMED_WAITINGTERMINATED。本文讲解线程状态变化的流程以及用代码演示通过调用哪些方法可以使线程改变状态。

一.简述6种线程状态定义

将线程的各种状态定义列出在此,方便大家查阅,下文再详细讲解。

  1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法时的状态。

  2. 运行(RUNNABLE):Java线程中将操作系统中的就绪(ready)和运行中(running)两种状态笼统的称为“可运行”RUNNABLE状态。
    线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。所以调用了start()方法并不意味这线程会立即执行。

  3. 阻塞(BLOCKED):表示线程阻塞于锁。仅在Synchronized代码块中,且没有获得锁的状态。
    等待的是其他线程释放排他锁。

  4. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
    无期限等待。等待被唤醒。

  5. 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
    有时间期限的等待。等待一段设置好的时间。

  6. 终止(TERMINATED):表示该线程已经执行完毕。
    线程正常的执行完毕,会进入到这种状态。或者是run()方法中出现了异常,线程会意外终止。

二.线程状态详细解析

1.初始状态

当创建一个线程后,线程就进入了初始状态。如何创建线程,参考文章:《Java官方文档创建线程的两种方式及优点和缺点分析》。

2.1运行状态(就绪状态)

就绪状态(ready)只是表名线程有资格运行,若调度程序没有挑选到此线程,此线程将永远是就绪状态。

怎样能够进入就绪状态?

①当调用线程的start()方法后,此线程即进入就绪状态。

②当线程执行完sleep()方法后,线程即进入就绪状态。

③其他线程执行Join()方法结束后,线程进入就绪状态。

④等待用户输入完毕,某个线程拿到对象锁,线程进入就绪状态。

⑤当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入就绪状态。

⑥锁池里的线程拿到对象锁后,进入就绪状态。

2.2.运行状态(运行中状态)

运行中状态(running)即线程调度程序从可运行池中选择一个线程执行。

怎样能够进入运行中状态?

只能由CPU选择任意一个就绪状态的线程执行,程序员无法手动指定运行哪个线程,这是线程进入运行中状态的唯一方式。

注意: 这里提到“选择哪个线程运行是CPU自己决定的”,这是Java官方给出的答案:The choice is arbitrary and occurs at the discretion of the implementation。译文:“这种选择是任意的,由执行者自行决定”。

代码演示NEW和RUNNABLE状态

public class ShowThreadRunnableStatus implements Runnable {public static void main(String[] args) {Thread thread = new Thread(new ShowThreadRunnableStatus());System.out.println("创建完线程的状态:" + thread.getState());thread.start();System.out.println("启动后线程的状态:" + thread.getState());try {// 线程执行2毫秒以后,打印一下状态Thread.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程执行中的线程状态:" + thread.getState());}@Overridepublic void run() {for (int i = 0; i < 800; i++) {System.out.println(i);}}
}

运行结果:

创建完线程的状态:NEW
启动后线程的状态:RUNNABLE
0
1
2
3
打印结果省略……
263
264
265
线程执行中的线程状态:RUNNABLE
266
267
268
打印结果省略……

3.阻塞状态

经常听到“线程阻塞”这个名词,那到底什么是阻塞呢?

BLOCKED或WAITING或TIME_WAITING这三种统称为堵塞状态。因为这三种状态,都让线程进入等待状态,而不再是运行状态,所以统称为堵塞状态。阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态,即还没有真正运行Synchronized修饰的代码时的状态。

通俗的讲,就是线程想要执行Synchronized代码块内容,但却没有拿到锁,此时等待时的状态就是堵塞状态。

生活例子:
就像是排队购物时,马上到你但还没到你的状态,就是堵塞状态。你被堵塞在购物窗口之前,当轮到你购买付款时,那就进入了运行状态。

4.等待状态

等待状态即执行了`wait()`方法后,将锁释放,进入无尽的等待当中,直到被`notify()`方法唤醒。

处于这种状态的线程CPU将不会给其分配执行时间,如果没有显式唤醒,将永远无期限的等待下去。

生活例子:
就像是面试官在面试结束后,然你回去等消息。如果面试官不主动联系你,那就说明你已经没有机会了,此时你就只能无期限的等下去。面试官一般不会打电话告诉你,你没有通过面试。此时对这个岗位的面试,就陷入了无尽的等待,这就是等待状态。

代码展示WAITING状态

public class ShowThreadWaitingStatus implements Runnable {public static void main(String[] args) {ShowThreadWaitingStatus runnable = new ShowThreadWaitingStatus();Thread thread1 = new Thread(runnable);thread1.start();Thread thread2 = new Thread(runnable);thread2.start();try {Thread.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}// 打印出TIME_WAITING,原因是:Thread.sleep(1000);System.out.println("线程1的状态:" + thread1.getState());// 打印出BLOCKED,因为线程2拿不到method的方法锁System.out.println("线程2的状态:" + thread2.getState());try {Thread.sleep(1400);} catch (InterruptedException e) {e.printStackTrace();}// 打印出WAITING,因为wait()方法,使线程进入到WAITING状态System.out.println("线程1的状态:" + thread1.getState());// 打印出TIME_WAITING,因为Thread.sleep(1400)方法,使线程进入到TIME_WAITING状态System.out.println("线程2的状态:" + thread2.getState());}@Overridepublic void run() {method();}/****/private synchronized void method() {try {Thread.sleep(1000);wait();//System.out.println(Thread.currentThread().getName() + ",状态是:" + Thread.currentThread().getState());} catch (InterruptedException e) {e.printStackTrace();}}
}

运行结果:(在不同的JVM机器上结果可能会不同)

线程1的状态:TIMED_WAITING
线程2的状态:BLOCKED
线程1的状态:WAITING
线程2的状态:TIMED_WAITING

5.超时状态

等待状态即执行了`wait(timeout)`方法后,将锁释放,进入等待当中,直到被`notify()`方法唤醒或者时间超过timeout设置的毫秒数后,自动唤醒。

等待状态跟超时等待状态的区别就是,超时等待可以设置一个超时时间,如果超过了这个时间,没有被唤醒,那就不等了,自动被唤醒。

线程何时被唤醒我们无法控制,但我们可以修改和优化线程执行时间。

我们不能控制的就不要纠结了,写代码要在边界内做事,我们可以优化代码,删除不必要的逻辑,来优化线程的执行速度。

生活例子:
就像被老师罚站,如果数学老师有急事离开了教室但忘了取消对你的惩罚,那你就只能一直被罚站,直到下课铃响起,即便数学老师没有让你坐下,你也可以自行解除封印,坐着学习下一节的语文课,这就是过了指定的时间后,自行唤醒。

代码演示TIME_WAITING和BLOCKED状态

public class ShowThreadBlockedStatus implements Runnable {public static void main(String[] args) {ShowThreadBlockedStatus runnable = new ShowThreadBlockedStatus();Thread thread1 = new Thread(runnable);thread1.start();Thread thread2 = new Thread(runnable);thread2.start();try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}//线程1的状态:TIMED_WAITING,因为:Thread.sleep(1000)System.out.println("线程1的状态:" + thread1.getState());// 打印出BLOCKED,因为拿不到method的方法锁System.out.println("线程2的状态:" + thread2.getState());}@Overridepublic void run() {method();}/****/private synchronized void method() {try {Thread.sleep(1000);//System.out.println(Thread.currentThread().getName() + ",状态是:" + Thread.currentThread().getState());} catch (InterruptedException e) {e.printStackTrace();}}

运行结果:

线程1的状态:TIMED_WAITING
线程2的状态:BLOCKED

6.终止状态

如何进入终止状态

①.当线程run()方法完成时,或者主线程的main()方法完成时,此时线程的状态就变成了终止状态,即便线程对象还存活着,还没有被垃圾回收,但是线程一旦终止,就不能复生。

注意: 如果在一个终止后的线程重新调用start(),会抛出java.lang.IllegalThreadStateException异常,说明线程不能死而复生。

②线程run()方法中抛出了异常,则会进入终止状态。

三.线程状态的变化和执行顺序

线程状态变化流程图

1.只能正序的三个状态

线程只能从NEWRUNNABLE在到TERMINATED,这是顺序不能倒序(上图最左侧的一列)。如果还想执行NEW,只能重新创建线程,原来的线程将被JVM回收。

2.可以逆向执行的三个状态

线程的RUNNABLEWAITINGRUNNABLETIME_WAITINGRUNNABLEBLOCKED是可以双向执行的。

3.线程状态不可跳跃执行

线程状态是不能跳跃的,NEW状态是不能跳跃到BLOCKEDWAITINGTIME_WAITING状态执行的(),NEW也不能直接跳跃到TERMINATED状态。

总结

本文详细讲解了线程的6种状态各自的定义,以及从代码层面演示并分析了各种状态的含义,讲解了线程执行的流程以及注意事项。涉及到线程的其他知识,请见下发补充。喜欢本文请点赞、收藏、关注。

参考资料补充:
关于多线程、synchronized关键字wait()notify()方法的系列教程,请参考以下文章:

《Java中synchronized实现类锁的两种方式及原理解析》

《Java中synchronized实现对象锁的两种方式及原理解析》

《Java多线程wait()和notify()系列方法使用教程》

《Java多线程中notifyAll()方法使用教程》

《Java两个线程交替打印奇偶数(两种方法对比)》

《Java中Synchronized的可重入性和不可中断性的分析和代码验证》

《Java多线程访问Synchronized同步方法的八种使用场景》

《Java官方文档创建线程的两种方式及优点和缺点分析》

《Java中线程安全和线程不安全解析和示例》

Java线程状态完全解析教程相关推荐

  1. 四种Java线程池用法解析

    四种Java线程池用法解析 本文为大家分析四种Java线程池用法,供大家参考,具体内容如下 http://www.jb51.net/article/81843.htm 1.new Thread的弊端 ...

  2. 面试必备,Java线程状态之细节回顾

    点击上方"方志朋",选择"设为星标" 做积极的人,而不是积极废人 来源:https://dwz.cn/vYqjShos Java线程有6种状态 在某个给定时间点 ...

  3. Java线程状态及 wait、sleep、join、interrupt、yield等的区别

    Java中的线程状态(详见Java线程状态及转换-MarchOn): wait:Object类的实例方法,释放CPU执行权,进入等待状态,直到  被中断.被拥有该对象锁的线程唤醒(notify或not ...

  4. java runnable wait_面试官:都说阻塞 I/O 模型将会使线程休眠,为什么 Java 线程状态却是 RUNNABLE?...

    摘要: 原创出处 https://studyidea.cn 「公众号:程序通事 」欢迎关注和转载,保留摘要,谢谢! 使用 Java 阻塞 I/O 模型读取数据,将会导致线程阻塞,线程将会进入休眠,从而 ...

  5. java 线程状态_Java线程为何没有Running状态?我猜你不知道。

    作者:国栋原文:https://my.oschina.net/goldenshaw/blog/705397 Java虚拟机层面所暴露给我们的状态,与操作系统底层的线程状态是两个不同层面的事.具体而言, ...

  6. java线程切换 notify_浅谈 Java线程状态转换及控制

    作者:城北有个混子 出自:博客园 1.线程的状态(系统层面) 一个线程被创建后就进入了线程的生命周期.在线程的生命周期中,共包括新建(New).就绪(Runnable).运行(Running).阻塞( ...

  7. Java 线程状态之 TIMED_WAITING

    定义 一个正在限时等待另一个线程执行一个动作的线程处于这一状态. A thread that is waiting for another thread to perform an action fo ...

  8. Java线程状态Jstack线程状态BLOCKED/TIMED_WAITING/WAITING解释

    一.线程5种状态 新建状态(New) 新创建了一个线程对象. 就绪状态(Runnable) 线程对象创建后,其他线程调用了该对象的start()方法.该状态的线程位于可运行线程池中,变得可运行,等待获 ...

  9. java 线程状态_面试官问:为什么Java线程没有Running状态?我懵了

    点击上方"占小狼的博客",选择"设为星标" 本文阅读时间大约4分钟. 来源:https://dwz.cn/dLRLBZab Java虚拟机层面所暴露给我们的状态 ...

最新文章

  1. DL动态加载框架技术
  2. 直流无刷电机制动的三种方式
  3. Spring - bean的lazy-init属性(懒加载)
  4. 关系数据库理论:数据库的六大范式知识笔记
  5. Statement与PreparedStatement的区别
  6. C++ Templates 中的一个例子
  7. FPGA常用单位换算
  8. BP神经网络原理简介
  9. 波形捕捉:(8)使用“捕捉缓冲区”
  10. 如何把你的图标转换成WEB字体
  11. Java中的双冒号::是什么玩意?有这个语法?
  12. plsql 64连接32oracle,32位plsql developer连接64位oracle的方法
  13. Linux下Intel网卡固件烧写工具
  14. NPT PT G螺纹
  15. linux切换声卡,Ubuntu中双声卡使用实例
  16. Vue3警告:[Vue warn] Extraneous non-emits event listeners (changeParentProps) were passed to component
  17. 第8周编程题在线测试
  18. 我欲封神——JAVA封神之路
  19. 虚数和复数更好的理解
  20. 艺赛旗RPA 网页处理系列(四):网页表格中获取数据,并点击相应记录后的按钮操作说明总结

热门文章

  1. r语言进行go富集分析_GO富集分析可视化:R语言GOplot包——准备自己的数据
  2. uniapp-本地图片分享-截图分享-解决打包之后图片分享为空
  3. biee java_BIEE URL登陆方法
  4. Android内存泄漏的简单检查与分析方法
  5. 为什么每个软件工程师都应该学习Python?
  6. 【学习分享】 记录开源小白的第一次 PR
  7. [数论]数论四大定理
  8. RESTEasy底层使用Netty
  9. error: invalid type argument of unary ‘*‘ (have ‘int‘) *__first = __tmp;
  10. 【转载】ACM入门 .