JDK 的 Thread 源码定义了6个状态: java.lang.Thread.State

  • New
    尚未启动的线程的线程状态。
  • Runnable
    可运行线程的线程状态,等待CPU调度。
  • Blocked
    线程阻塞等待监视器锁定的线程状态。处于synchronized同步代码块或方法中被阻塞。
  • Waiting
    等待线程的线程状态。下列不带超时的方式:
    Object.waitThread.joinLockSupport.park
  • Timed Waiting
    具有指定等待时间的等待线程的线程状态。下列带超时的方式:
    Thread.sleep、0bject.wait、 Thread.join、 LockSupport.parkNanos、 LockSupport.parkUntil
  • Terminated
    终止线程的线程状态。线程正常完成执行或出现异常。

文字说的还不是太清楚了,让我来你画个图就一目了然了:

  • Thread状态机
  • 上面那个图太复杂了看不懂?没问题,看个小学生版:

1 NEW

  • 线程还没有开始执行

    实现Runnable接口和继承Thread可以得到一个线程类,new一个实例出来,线程就进入了 NEW 状态。

当调用线程的start()方法,线程也不一定会马上执行,因为Java线程是映射到os的线程执行的,此时可能还需要等os调度,但此时该线程的状态已经为RUNNABLE。

2 RUNNABLE

只是说你有资格运行,调度程序没有挑选到你,你就永远是可运行状态。

2.1条件

  • 调用start()
  • Thread.sleep(long millis)

    一定是当前线程调用此方法,当前线程进入阻塞,不释放对象锁,millis后线程自动苏醒进入可运行态。
    作用:给其它线程执行机会的最佳方式。
  • 其他线程join()结束
    当前线程里调用其它线程1的join方法,当前线程阻塞,但不释放对象锁,直到线程1执行完毕或者millis时间到,当前线程进入可运行状态。
  • 等待用户输入完毕
  • 某个线程拿到对象锁
  • 当前线程时间片用完
  • Thread.yield()

    调用当前线程的yield()
    一定是当前线程调用此方法,当前线程放弃获取的cpu时间片,由运行状态变会可运行状态,让os再次选择线程。
    作用:让同优先级的线程轮流执行,但并不保证一定会轮流执行。实际无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞。
  • 锁池里的线程拿到对象锁后,进入可运行状态
  • 正在执行的线程

该状态最有争议,注释说它表示线程在JVM层面是执行的,但在os不一定,它举例是CPU,毫无疑问CPU是一个os资源,但这也就意味着在等os其它资源时,线程也会是这个状态。

I/O阻塞算是等os的资源?

3 BLOCKED

  • 线程由于等待监视器锁,被阻塞。 处于阻塞态的线程在调用Object.wait之后正在等待监视器锁 进入 同步的块/方法或 再进入 同步的块/方法

被挂起,线程因为某原因放弃cpu 时间片,暂时停止运行。

3.1条件

  • 当前线程调用Thread.sleep()
  • 运行在当前线程里的其它线程调用join(),当前线程进入阻塞态
  • 等待用户输入时,当前线程进入阻塞态

3.2 分类

  • 等待阻塞
    运行的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)

  • 同步阻塞
    运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)

  • 其他阻塞
    运行的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。
    当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。

线程在阻塞等待monitor lock(监视器锁)
一个线程在进入synchronized修饰的临界区的时候,或者在synchronized临界区中调用Object.wait然后被唤醒重新进入synchronized临界区都对应该态。

结合上面RUNNABLE的分析,也就是I/O阻塞不会进入BLOCKED状态,只有synchronized会导致线程进入该状态

关于BLOCKED状态,注释里只提到一种情况就是进入synchronized声明的临界区时会导致,这好理解,synchronized是JVM自己控制的,所以这个阻塞事件它自己能够知道(对比理解上面的os层面)。

interrupt()是无法唤醒的,只是做个标记。

4 等待

  • 等待线程的线程状态。

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

例如:

  • 一个在对象上调用Object.wait()的线程正在等待另一个线程在该对象上调用Object.notify()或Object.notifyAll() 。这样便可以控制线程的执行顺序。
  • Thread.join()的线程正在等待指定的线程终止
  • 线程拥有对象锁后进入到相应的代码区后,调用相应的“锁对象”的wait()后产生的一种结果

进入条件

由于调用以下方法之一,线程处于等待状态:

  • Object.wait()
  • LockSupport.park()
  • Thread join( )

它们也是在等待另一个对象事件的发生,也就是描述了等待的意思。

WAIT V.S BLOCKED

  • BLOCKED
    JVM认为程序还不能进入某区域,因为同时进去就会有问题,这是一块临界区
  • wait()
    先决条件是要进入临界区,即线程已经拿到“凭证”,自己可能进去做了一些事情,但此时通过判定某些业务上的参数,发现还有一些其他配合的资源没有准备充分,那么自己就等等再做其他事

典型案例就是通过wait()/notify()完成生产者/消费者模型。
当生产者生产过快,发现仓库满了,即消费者还没有把东西拿走(空位资源还没准备好) 时,生产者就等待有空位再做事。
消费者拿走东西时会发出“有空位了”的消息,那么生产者就继续工作。
反之,当消费者消费过快发现没有存货时,消费者也会等存货到来,生产者生产出内容后发出“有存货了”的消息,消费者才继续抢东西。

这种状态下,若发生了对该线程的interrupt()是有用的,处于该状态的线程内部会抛InerruptedException,该异常应当在run()里面捕获,使得run()正常执行完成。
run()内部捕获异常后,还可以让线程继续运行,根据具体场景决定。

这种状态下,若某线程对该锁对象做了notify(),则将从等待池中唤醒一个线程重新恢复到RUNNABLE
notify()外,还有一个notifyAll() ,前者是唤醒一个处于WAITING的线程,而后者是唤醒所有的线程。

Object.wait()是否需要死等呢?

不是,除中断外,它还有两个重构方法

  • Object.wait(int timeout)
    传入的timeout 参数是超时的毫秒值,超过该值后会自动唤醒,继续做下面的操作(不会抛InterruptedException ,但是并不意味着我们不去捕获,因为不排除其他线程会对它做interrup())
  • Object.wait(int timeout,int nanos)
    更精确的超时设置,可以精确到纳秒,这个纳秒值可接受的范围是0~999999 (因为100000onS 等于1ms)。

同样的
LockSupport park( )
LockSupport.parkNanos( )
LockSupport.parkUntil( )
Thread.join()
这些方法都会有类似的重构方法来设置超时,达到类似的目的,不过此时的状态不再是WAITING,而是TIMED.WAITING

通常写代码的人肯定不想让程序死掉,但是又希望通过这些等待、通知的方式来实现某些平衡,这样就不得不去尝试采用“超时+重试+失败告知”等方式来达到目的。

5 TIMED_WAITING


当调用Thread.sleep()时,相当于使用某个时间资源作为锁对象,进而达到等待的目的,当时间达到时触发线程回到工作状态。

进入条件

  • LockSupport parkNanos(long nanos)
  • LockSupport parkUntil(long deadline)

这个线程对象也许是活的,但是,它已经不是一个单独执行的线程,在一个死去的线程上调用start()方法,会抛java.lang.IllegalThreadStateException

线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。
run()走完了,线程就处于这种状态。其实这只是Java 语言级别的一种状态,在操作系统内部可能已经注销了相应的线程,或者将它复用给其他需要使用线程的请求,而在Java语言级别只是通过Java 代码看到的线程状态而已。

为什么wait( )notify( )必须要使用synchronized

如果不用就会报ilegalMonitorStateException,常见写法如下:

synchronized(Object){object.wait() ;//object.notify() ;
}synchronized(this){this.wait();
}
synchronized fun( ){this.wait();//this.notify();
}

wait()notify()是基于对象存在的。

  • 为什么要基于对象存在呢?
    既然要等,就要考虑等什么,这里等待的就是一个对象发出的信号,所以要基于对象而存在

不用对象也可以实现,比如suspend()/resume()就不需要,但是它们是反面教材,表面上简单,但特别容易死锁。

所以目前它调用的方式只能是Object.wait(),这样才能和对象挂钩。但这些东西还与问题“wait()/notify() 为什么必须要使用synchronized" 没有半点关系,或者说与对象扯上关系,为什么非要用锁呢?

既然是基于对象的,就得用个数据结构存放这些等待的线程,该数据结构应当与该对象绑定(查看JVM代码,可知该数据结构为一个双向链表),此时在这个对象上可能同时有多个线程调用wait()/notify(),在向这个对象所对应的双向链表中写入、删除数据时,依然存在并发的问题,理论上也需要一个锁来控制。在JVM 内核源码中并没有发现任何自己用锁来控制写入的动作,只是通过检查当前线程是否为对象的OWNER 来判定是否要抛异常。由此可见它希望该动作由Java 程序代码自己控制,为什么JVM不选择自己控制锁呢?
因为有时更低抽象层次的锁不是好事,这样的请求对于外部可能是反复循环地去征用,或这些代码还可能在其他地方复用,也许将它粗粒度化会更好一些,而且这样的代码在写在Java 程序中也更加清晰,容易看到相互之间的关系。

interrupt()操作只对处于WAITINGTIMED_WAITING 状态的线程有用,让它们产生实质性的异常抛出。
通常如果线程处于运行中状态,也不会让它中断,如果中断是成立的,可能会导致正常的业务运行出现问题。另外,如果不想用强制手段,就得为每条代码的运行设立检查,但是这个动作很麻烦,JVM 不愿意做这件事情,它做interrupt()仅仅是打一个标记,此时程序中通过isInterrupt()方法能够判定是否被发起过中断操作,如果被中断了,那么如何处理程序就是设计问题。

比如,若代码运行是一个死循环,则在循环中可以这样:

while(true) {if (Thread.currentThread.isInterrupt()) {//可做类似的break、return,抛出InterruptedException 达到某种目的,这完全由自己决定//如拋出异常,通常包装一层try catch 异常处理,进一步做处理,如退出run或什么也不做}
}
  • 这太麻烦了,为什么不可以自动呢?
    可以这么理解:当你发现门外有人呼叫你,你自己是否愿意搭理他是你的事情,这是一种有“爱”的沟通方式,反之是暴力地破门而入,把你强制“抓”出去

JDK 6 及以后,可以使用线程的interrupted( )

判定线程是否已经被调用过中断方法,表面上的效果与isInterrupted()
结果一样,不过这是个静态方法。
此外,这个方法调用后将会重新将中断状态置false,方便循环利用线程,而不是中断后状态就始终为true,就无法将状态修改回来了。
对应的,判定线程的相关方法有isAlive()

6 Terminated

  • 最后,再回顾一个线程的状态图

华为技术专家深度解析Java线程状态相关推荐

  1. 深入源码,深度解析Java 线程池的实现原理

    java 系统的运行归根到底是程序的运行,程序的运行归根到底是代码的执行,代码的执行归根到底是虚拟机的执行,虚拟机的执行其实就是操作系统的线程在执行,并且会占用一定的系统资源,如CPU.内存.磁盘.网 ...

  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虚拟机层面所暴露给我们的状态 ...

  10. 【图解】透彻Java线程状态转换

    大家好,我是阿星,好久不见,欢迎来到Java并发编程系列番外篇线程状态转换,内容通俗易懂,请放心食用. 线程状态 先来个开场四连问 Java线程状态有几个? Java线程状态是如何转换? Java线程 ...

最新文章

  1. 新手篇——学习网页开发需要多长时间就能找到工作
  2. .net 同步mysql_MySQL服务器主从数据库同步配置
  3. [How TO]-图解virtualbox下安装ubuntu20.04虚拟机
  4. Linux进程实践(4) --wait避免僵尸进程
  5. 【JVM调优工具篇】使用MAT工具分析dump文件(查看GC Roots)
  6. 商品品牌信息的增删改查操作步骤_javaweb09-Servlet增删改查
  7. ssh首次连接时提示yes/no
  8. 编码实战Web端联系人的增删改查
  9. SQLite中利用事务处理优化DB操作
  10. CUDA C编程权威指南 第七章 调整指令级原语
  11. ffmpeg libx264_FFmpeg之FFmpeg模块介绍(一)
  12. windows服务器漏洞修复,Windows7系统漏洞怎么修复?
  13. 递归回溯法求数独全部解
  14. RabbitMq Direct exchange路由模型
  15. 有关图像生成的函数 .
  16. UML测试题(UML基础)
  17. 纯靠成绩毫无科研的保研历程(电子信息工程专业)
  18. 计算机网络实验【路由器的基本配置】
  19. idea Translation翻译插件失效解决办法
  20. 英语四级真题作文 计算机,2017年6月英语四级作文真题及范文:卖电脑

热门文章

  1. android 串流 ps4,就想要玩游戏!PS4有线串流到笔记本电脑实战
  2. 创新检查技术,赋能保密监管 ,您需要一款这样的数据库内容保密检查系统!
  3. 字体字号磅数大小对照表
  4. java毕业生设计大学生旅游拼团网站计算机源码+系统+mysql+调试部署+lw
  5. pdf编辑器如何在pdf上修改
  6. 未能加载文件或程序集问题
  7. python xlwt_python – 使用xlwt写入现有工作簿
  8. H5活动页面抽奖源码
  9. cdrx8如何批量导出jpg_cdrx8如何批量导出jpg_办公软件操作技巧022:如何从word文档中批量导出多张图片......
  10. 使用VUE前端开发Lodop打印程序,实现网页打印模块