线程状态的枚举(Thread.State)
这6种状态被明确地定义在Thread类的一个内部枚举类Thread.State中:
- NEW(新建)
一个尚未启动的线程处于这一状态。(A thread that has not yet started is in this state.) - RUNNABLE(可运行)
一个正在 Java 虚拟机中执行的线程处于这一状态。(A thread executing in the Java virtual machine is in this state.) - BLOCKE(阻塞)
一个正在阻塞等待一个监视器锁的线程处于这一状态。(A thread that is blocked waiting for a monitor lock is in this state.) - WAITING(等待)
一个正在无限期等待另一个线程执行一个特别的动作的线程处于这一状态。(A thread that is waiting indefinitely for another thread to perform a particular action is in this state.) - TIMED_WAITING(计时等待)
一个正在限时等待另一个线程执行一个动作的线程处于这一状态。(A thread that is waiting for another thread to perform an action for up to a specified waiting time is in this state.) - TERMINATED(终止)
一个已经退出的线程处于这一状态。(A thread that has exited is in this state.)
注:以上内容直接来自State类中的Javadoc,具体的描述后面再分析。
它上面还有个注解”@since 1.5”,暗示了在JDK1.5时引入了这些定义。因此,说有6种线程状态是对1.5及以后版本而言
Thread类中有一个方法:getState,返回的就是这里定义的状态。
可用于监控工具分析线程状态,不作同步工具使用
线程状态的监控
一些监控工具,如VisualVM,JConsole 都可以监控线程状态。
上图中勾选了一个叫”Worker-JM”的线程,可以看到它处于WAITING状态下,具体在下面的waiting on上显示了它在一个ArrayList对象上等待。
注:截图中下方的”Thread inspector”窗体是一个插件,默认是没有安装的,可在”菜单–工具–插件”下面找到它并安装即可。
虚拟机线程状态 vs 操作系统线程状态
首先要明确一点,这里所谓的”Java线程状态”指的是虚拟机层面上暴露给我们的状态,这些状态是由枚举类Thread.State明确定义的。
你可能听说过这样的说法,比如在Windows系统下,很多的虚拟机实现实际都把Java线程一一映射到操作系统的内核线程(kernel thread)上。
除了1:1映射,还可能有N:1或者N:M映射。总之,世界很乱。。。
自然,操作系统的线程也有它自己的状态。你Java有6种,Windows可能有N种,到了Linux系统,它可能有M种。
好吧,我也不知道具体有几种~且不说操作系统各种版本满天飞。
在这里我想说的是,你管它是N种还M种!虚拟机层的存在,统一了这些差别,不管它是N还是M种,到了Java层面它们都被映射到了6种状态上来。自然,两个层面上有很多状态其实是大同小异的。至于具体差异,那是写虚拟机实现的那些家伙们去操心的事。
有可能操作系统中的两种状态在JVM中则统一成了一种状态,也可能操作系统中的一种状态在JVM中又细分成了两种状态,谁知道呢?你也不想去知道,反正我是不想去知道。
而很多关于操作系统上的书则常会提到有5种进程(process)状态:new,ready,running,waiting,terminated。
这里所谓”进程状态”指早期的那种”单线程进程”的状态。
对于现在普遍的”多线程进程”,显然,谈论”进程状态”已经没有意义,应该谈论”进程下某个线程的状态”或者直接说”线程状态”。不过有时还是会把”进程状态”和”线程状态”混着去说。
有些系统把线程叫成”轻量级进程”(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层面上。
具体而言,比如当说到WAITING状态时,指的就是Thread.State.WAITING。
有了以上基础,才能在接下来更好地去分析这6种状态。
具体状态分析
接下来再来细看这6个状态,首先从简单的谈起。
- NEW
当使用new Thread()创建一个新的线程,又还没有开始执行(not yet started)它的时候就处于NEW状态。
这里所谓”开始执行”具体指调用线程类中的start方法。
注意:你不能直接调用run方法,这样的话还是在原线程上执行。只有调用start方法才会开启新的执行线程,接着它会去调用run。
在start之后,线程进入RUNNABLE状态,之后还可能会继续转换成其它状态。
注:一个线程只能被start一次。
- RUNNABLE
前面有提到,它指”正在Java虚拟机中执行”。
3. BLOCKED
等待监视器锁(waiting for a monitor lock )
这是一种特殊的waiting,实际上就是被synchronized方法或者块阻塞。
monitor有些书上通常叫”管程”,我也不太确定要怎么叫它。这里叫成”监视器”也是取字面的意思。
4. WAITING
无限期等待另一个线程执行一个特别的动作。(waiting indefinitely for another thread to perform a particular action )
这里所谓的动作通常即是指”notify或是notifyAll”。
- TIMED_WAITING
限时等待另一个线程执行一个动作。(waiting for another thread to perform an action for up to a specified waiting time )
如果没有等到如”notify”之类的动作,时间到了也会自动退出这一状态