关于 Java 的线程状态

摘要:初步谈论了Java中的6种线程状态,并探讨了它们与操作系统线程状态的异同。

目录
[隐藏]

Java 线程有 6 种状态。在某个给定时间点上,一个线程只能处于这 6 种状态中的一种。

线程状态的枚举:Thread.State

这 6 种状态被明确地定义在 Thread 类的一个内部枚举类 Thread.State 中:

image

它们是:

  • NEW (新建)
    A thread that has not yet started is in this state.
  • RUNNABLE (可运行)
    A thread executing in the Java virtual machine is in this state.
  • BLOCKED (阻塞)
    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,如果进行了完整的 JDK 包安装,比如在我的机器上,可以在 C:\Program Files\Java\jdk1.7.0_25\bin 中找到它:

image

它有个漂亮的小图标,可以很容易就找到它,双击就可以打开它了:

image

可以看到它自身也是 Java 应用(它还能监控它自身呢),bin 下的 exe 可能仅是个引导的程序。还有就是我们熟悉的 Eclipse 了。

界面不咋地,一看就是典型的 Java GUI 的风格……

双击上图中本地列表下的 Eclipse,则出现如下监控界面,选择到“线程”的 tab 上:

image

上图中勾选了一个叫“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。

os thread state

不幸的是,有很多的书上常常把这些进程状态,线程状态与 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.

这些状态是虚拟机状态,它不反映任何操作系统的线程状态。

它对应的内核线程中的状态可能有 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 一次。

TERMINATED

终止状态,这个也没什么好说的,完成了执行后(completed execution)或者说退出了(exited)。线程就进入了终止状态。

其它状态

余下的几个状态,由于无法简单几句说完,这里先作些简介:

RUNNABLE:前面有提到,它指“正在 Java 虚拟机中执行”。

BLOCKED:等待监视器锁(waiting for a monitor lock )。

这是一种特殊的 waiting,实际上就是被 synchronized 方法或者块阻塞。

monitor 有些书上通常叫”管程“,我也不太确定要怎么叫它。这里叫成”监视器“也是取字面的意思。

WAITING:无限期等待另一个线程执行一个特别的动作(waiting indefinitely for another thread to perform a particular action )。

TIMED_WAITING:限时等待另一个线程执行一个动作(waiting for another thread to perform an action for up to a specified waiting time )。

详细地介绍留在后面再谈。

发表评论

电子邮件地址不会被公开。