老哥你知道JAVA线程到底有多少个状态吗?5大状态?6大状态?7大状态?

背景

掌握多线程,并发,锁是一个优秀的程序员必备的知识,他们都是基于线程的而有意义,熟悉并且理解线程的机制是非常重要的。

今天我们来聊一聊,线程有几个状态?

如果你去网上冲浪一下,会发现各说纷纭,5 大状态、6 大状态、7 大状态、新建、就绪、可执行、运行、阻塞、锁池、挂起、中断、等待、结束、死亡、停滞,看到这些名词,你一定晕了吧?

JAVA 定义的状态

如果你打开 Thread 类,找到他下面的枚举类 State,你会发现 JAVA 只定义了以下 6 种状态:

  • New(新建)
  • Runnable(运行)
  • Blocked(阻塞)
  • Waitting(无限期等待)
  • Timed Watting(限期等待)
  • Terminated(终止)

这里的状态名词翻译参考了 周志明的《深入理解 Java 虚拟机》

下面我会对这些状态进行逐一分析,并且解释为什么出现上面那么多的状态

New(新建)

新创建了一个线程对象,但还没有调用 start()方法。

Runnable(运行)

Runnable 包括了操作系统线程中的 Running(运行中)和 Ready(就绪)

  • Ready(就绪)

线程对象创建后,其他线程(比如 main 线程)调用了该对象的 start()方法

该状态的线程位于可运行线程池中,等待被线程调度选中,获取 CPU 的使用权,也就是说有了被 CPU 运行的资格

  • Running(运行中) 就绪状态的线程在获得 CPU 时间片后转换为运行中(Running)

Blocked(阻塞)

在等待着获取一个排它锁(例如:synchronized),这个事件将在另外一个线程放弃这个锁的时候发生

Waitting(无限期等待)

这种状态的线程不会被分配 CPU 执行时间,不给过无需等待被他其他线程显示地唤醒,在一定时间之后他们会被系统自动唤醒

以下方法会让线程进入无限期等待状态

  • 没有设置 Timeout 参数的 Object.wait()
  • 没有设置 Timeout 参数的 Thread.join()

Timed Waitting(限期等待)

这种状态的线程不会被分配 CPU 执行时间,不给过无需等待被他其他线程显示地唤醒,在一定时间之后他们会被系统自动唤醒

以下方法会让线程进入限期等待状态

  • Thread.sleep()
  • 设置了 Timeout 参数的 Object.wait()
  • 设置了 Timeout 参数的 Thread.join()
  • LockSupport.parkNanos()方法
  • LockSupport.parkUnit()方法

Terminated(终止)

已终止的线程状态,线程已经终止运行

状态转换

  • 下图是《深入理解 JAVA 虚拟机》线程的状态转换图
    上面这张图比较好理解,但是没有暴露太多运行状态的细节
  • 我们来看下一个图
    这张图把运行状态中的(运行中和就绪的状态的转换)画了出来,这个内部状态是 CPU 去调度的。没有显性的在 JAVA 状态枚举里定义

相关容易混淆的问题解答

造成那么多名词的原因是什么?

  • 名词翻译问题
    • 比如 Terminated 翻译为终止,但是他的意义跟死亡有点类似,大家就传着传者名词叫 Dead 了(死亡)
  • 含义混淆
    • 例如 Runnable 里的内部状态 Ready 和 Running,这两个状态是 CPU 调度去关心的,但是枚举里想屏蔽这个东西。
    • 例如 Thread.suspend()这个方法,翻译过来就是挂起,有的同学就说线程有一个挂起状态,这明显就是把一个行为当成一个状态,没有理解线程的状态

什么是锁池?什么等待池?什么是等待队列?什么是同步队列?这几个有什么区别?

大家自己 Thinking 一下,假如现在有 10 个线程,现在有一个同步代码块的方法,同时只能有一个线程进入对吧?那其他 9 个线程都是阻塞状态, 那这 9 个线程放在哪里呢?

当第一个线程运行完之后,其他的阻塞的线程可是要竞争的,对吧?所以我们这边说阻塞的线程是放到锁池里的,那么这个锁池是怎么实现的呢? 这么一想就能明白了,它的内部是一个队列,也就叫同步队列。

同时类比一下,什么叫等待池,等待队列?其实跟之前的锁池,同步队列一个道理。

这里我并没有在 Google上找到关于锁池和同步队列比较官方的东西,很多资料也是一带而过,鸟大的可以给小弟普及下。

为什么有的博客里,把 Time Waiting、Waitting、Blocked 这三个状态统一成 Blocked,这种说话对么?

如果我们换一种角度来想问题,当线程状态是 Time Waiting 的时候,线程有干活吗?Waitting 的时候线程有干活吗?Blocked 线程有干活吗?

没错有人说了,Wait/Notify 是 Waitting 状态的转换,Synchronized 是 Blocked 状态的转换?但是他实质上不都是线程不干活吗?

所以我认为上面那个问题,其实还是有点道理的,那么JAVA为什么要把这几个状态区分呢?

在B乎找到了一个答案看起来还以自恰一点。原文在这里,赵老师的回答

这里我简单摘要一下

  • blocked 是过去分词,意味着他是被卡住的(无辜啊,全是泪)。因为这段代码只让一条线程运行。同时,jvm 是知道怎么结束 blocked 的,只要别的线程退出这段代码,他就会自动让你进去。也就是说别的线程无需唤醒你,由 jvm 自动来干。
  • waiiting 是说我调用 wait()等函数,主动卡住自己(我在等一个白富美),请 jvm 在满足某种条件后(白富美发消息让我们晚上见),比如另条线程调用了 notify()后,把我唤醒。这个唤醒的责任在于别的线程(白富美)明确的调用一些唤醒函数。

做这样的区分,是 JVM 出于管理的需要,做了这种区分,比如两个原因的线程放两个队列里管理(上面讲到的同步队列和等待队列),如果别的线程运行出了 synchronized 这段代码,我只需要去Blocked队列,放个出来。而某人调用了 notify(),我只需要去 Waitting 队列里取个出来。


文章参考:

  • 线程状态解释参考 《深入理解JAVA虚拟机》-周志明
  • 第二张线程状态转换图来自:https://blog.csdn.net/pange1991/article/details/53860651/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值