先看图增加点记忆。
-
Java线程状态:
-
线程状态转换图:
背景知识
JAVA的线程模型与操作系统线程的对应关系是1:1的,线程的调度权是由操作系统控制的。
为什么java的线程状态与操作系统不一致?
JVM线程状态:RUNNABLE
JVM的线程状态 RUNNABLE 覆盖了操作系统中的 READY 和 RUNNING 两种状态,这是因为 JVM 对线程调度和管理的抽象和粒度与操作系统不同。
在JVM中,RUNNABLE 状态表示线程可以被执行。这种状态表明线程已经就绪,可能正在运行,也可能等待CPU资源。
RUNNABLE 状态实际上表示两种可能的情况:
- 线程已经就绪,等待操作系统的调度分配CPU时间片,这对应操作系统的 READY 状态。
- 线程正在被操作系统调度并且正在执行中,这对应操作系统的 RUNNING 状态。
操作系统中的线程状态
操作系统的线程状态更细化,因为调度权利在操作系统手中,其随时可以把控调度的细节。其中:
READY:线程已经准备好执行,但暂时没有被分配CPU。
RUNNING:线程正在被CPU执行。
因此,综上所述,JVM的RUNNABLE = 操作系统的READY + RUNNING原因主要有以下几点:
a. JVM与操作系统的抽象粒度不同
JVM 是运行在操作系统之上的虚拟机,负责管理 Java 应用的生命周期和资源调度。JVM 并不直接管理CPU资源,而是依赖操作系统的线程调度器来分配时间片。因此,JVM 中的 RUNNABLE 代表的是逻辑上可以被执行的状态,而 JVM 无法直接区分线程是正在被操作系统调度(即 RUNNING),还是已经准备好但尚未被调度(即 READY)。
这种设计简化了JVM对线程的管理,JVM只需要关心线程是否有资格被调度,而不需要深入到操作系统层面来判断线程是正在运行还是等待CPU调度。
b. 跨平台设计的简化
JVM是跨平台的,它在不同操作系统上运行,但操作系统的线程状态和调度机制可能略有不同。为了保持跨平台的一致性,JVM不区分操作系统的 READY 和 RUNNING 状态,而是将它们统一映射为 RUNNABLE。这样,JVM的线程模型可以在不同的操作系统上保持一致性。
c. 操作系统负责实际的调度
线程在操作系统中的调度是由操作系统的调度器来管理的,操作系统通过上下文切换来在 READY 和 RUNNING 之间切换线程。JVM的 RUNNABLE 只是表示“线程已准备好被操作系统调度”,而实际的调度过程由操作系统完成。因此,JVM没有必要区分这两个状态。
手动分割线,另外说一点题外话:
-
如果jvm的线程状态与操作系统保持一致,抛开设计的复杂度和跨平台兼容性不谈。 试想一下这些状态的被设计出来的目的是什么?
为了让使用者更加准确获知线程的执行细节和当前状态,为排查问题提供便利性和指导方向。尤其是waitting、blocked这些状态,已经能够明确帮助我们在出现问题时进行排查分析了。 -
如果加上了ready和running状态,会有什么不一样?
使用时间片轮转抢占式调度的操作系统,每次分配的时间片只有几十毫秒,试想在这样的情况下还需要不停地修改用户态jvm线程的状态,并且最终在监控中展示和记录。 当真正需要使用的时候,你就会惊奇的发现状态一直在不停地改变,而且对于一个没有调度权利的程序而言,什么也做不了。
增加之后的优势在于,正是因为jvm线程状态的这种模糊性,以至于出现问题当需要对某个线程分析的时候,打印出来的线程栈信息中无法准确知道当前究竟是在等待CPU资源还是正在运行状态,因此会无法直观获得结果。需要借助于操作系统负载等等信息猜测原因。例如是否产生饥饿、堆积导致迟迟未被调度。
总而言之,相较于目前设计而言,增加了ready和running状态之后的收益其实并不明显,因为jvm并不能控制操作系统的调度策略。