虚拟机线程状态 vs 操作系统线程状态

线程的状态一文中我们曾经介绍了线程的状态,注意这里描述的是Java中定义的6种状态,是虚拟机层面上暴露给我们的状态,这些状态是由枚举类Thread.State中明确定义的。

也许有同学就要问了,为什么我之前还听说过线程只有只有5种状态呢?是哪里有问题吗?其实,不论是6种状态还是5种状态,这两种说法都是对的,只不过他们描述的对象不同,一个是Java虚拟机的线程状态,一个是操作系统中的线程状态。

本文主要介绍Java虚拟机线程状态和操作系统线程状态的关系。

你可能听说过这样的说法,比如在Windows系统下,很多的虚拟机实现实际都把Java线程一一映射到操作系统的内核线程kernel thread上。当然,除了1:1映射,还可能有N:1或者N:M映射。总之,世界很乱。。。

自然,操作系统的线程也有它自己的状态。你Java有6种,Windows可能有N种,到了Linux系统,它可能有M种,加上各种操作系统版本满天乱飞,我也不知道具体有多少种了…..

但是!!虚拟机层的存在,统一了这些差别,不管它是N还是M种,到了Java层面它们都被映射到了6种状态上来。因此,两个层面上有很多状态其实是大同小异的。

我们前面提到的5种状态,其实是很多操作系统教科书上的“官方说法”,而且指的是进程的状态。

进程状态图

不幸的是,有很多的书上常常把这些进程状态,线程状态与Java线程状态混在一起谈。

这里所谓“进程状态”,指早期的那种“单线程进程”的状态。对于现在普遍的“多线程进程”,显然,谈论“进程状态”已经没有意义,应该谈论“进程下某个线程的状态”或者直接说“线程状态”。

不过有时还是会把“进程状态”和“线程状态”混着去说。有些系统把线程叫成“轻量级进程”(light-weight process),所以还是在谈“进程状态”。有时则甚至既不叫“进程”,也不叫“线程”,它们叫“task”或者“job”。

总之还是有些乱的,我们不妨就拿Windows系统为例,用的就是“进程”和“线程”这两种较为标准的叫法,这时一个进程下至少有一个线程,线程是CPU调度的基本单位,进程不参与CPU调度,CPU根本不知道进程的存在。你在“任务管理器”中看到的所谓“进程状态”,跟线程状态不是一回事。

至于Java线程的状态,有的说有4种状态,有的说有5种,各种各样的说法都有。

比如看到Java只有RUNNABLE(可运行的)状态,就觉得这还不够呀,应该还有Running(运行中)状态;

又或者觉得RUNNABLE就是Running,所以应该还有个Ready(就绪)状态才对。

然而这些说法都是不准确的!如果我们读下Thread.State源码中的注释中,它说得很清楚:

These states are virtual machine states which do not reflect any operating system thread states。
这些状态是虚拟机状态,它不反映任何操作系统的线程状态。

一个 Java 线程它所对应的操作系统内核线程中的状态可能有Running又有Ready,但在虚拟机层面则统一映射成了RUNNABLE。如果Java中觉得没必要去区分这些,我们又何必去纠结这些呢?

以RUNNABLE为例,源码中的注释是这样说的:executing in the Java virtual machine(正在Java虚拟机中执行)
至于它是否真正在执行,不是我们要操心的事。

还有的情况则比如把Java状态中的BLOCKED,WAITING,TIMED_WAITING三种状态都笼统地称为blocked或者waiting;

操作系统也许只有一种状态,但这一次,Java作了细分,给出了三种状态。

很多声称Java线程只有4种或5种状态常常都是自作主张地合并了这些状态,把这些东西混为一谈是非常容易引发混乱的。我们将会在后面具体地谈到。

又或者把TIMED_WAITING当作不存在,从来不提有这个状态。

显然,这种做法又是受到传统进程状态划分的影响。尽管它与WAITING很像,我们最好按着Thread.State中的定义来,不要自己随意发挥。

综上所述,为避免出现混乱,厘清概念所处的层次是非常重要的。如无特别说明,讨论均在JVM层面上。