Java线程状态梳理

Author Avatar
第五季 2016.4.23
字数:2,165字 时长:9分钟
  • 微信扫一扫分享

  很多时候我们很自然的创建一个线程,紧接着启动它执行任务,然后对它不闻不问直到它替我们完成任务。那么从我们start一个线程到结束的这个过程中它都经历过什么呢?对于这个问题我们可以从线程的状态入手解答。

线程的状态

  首先线程的状态在源码中是以Thread类的内部类来定义的,它是一个枚举类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/**
* A thread state. A thread can be in one of the following states:
* <ul>
* <li>{@link #NEW}<br>
* A thread that has not yet started is in this state.
* </li>
* <li>{@link #RUNNABLE}<br>
* A thread executing in the Java virtual machine is in this state.
* </li>
* <li>{@link #BLOCKED}<br>
* A thread that is blocked waiting for a monitor lock
* is in this state.
* </li>
* <li>{@link #WAITING}<br>
* A thread that is waiting indefinitely for another thread to
* perform a particular action is in this state.
* </li>
* <li>{@link #TIMED_WAITING}<br>
* A thread that is waiting for another thread to perform an action
* for up to a specified waiting time is in this state.
* </li>
* <li>{@link #TERMINATED}<br>
* A thread that has exited is in this state.
* </li>
* </ul>
*
* <p>
* A thread can be in only one state at a given point in time.
* These states are virtual machine states which do not reflect
* any operating system thread states.
*
* @since 1.5
* @see #getState
*/
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,

/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,

/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,

/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING,

/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,

/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}

  这么长的类,其实就只有6个枚举值剩下的全部是注释,是的,可见这简简单单的几个值包含了多少信息;如果让我们来写肯定一行就完了,类似于这样的精炼:

1
2
3
public enum State {
NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED;
}

  ok,不调侃了,毕竟jdk的作者都是大神。下面就来详细介(fan)绍(yi)一下这几个状态:

NEW(新建)

  线程还没有开始执行,即是在star调用之前的一种状态,那显而易见就是在声明一个线程对象的时候咯。
  首先,既然已经有状态了,那肯定是已经创建好线程对象了,如果对象都没有,怎么会有状态。我们都知道当调用线程的start()方法时,线程不一定会马上执行,因为Java线程是映射到操作系统的线程进行执行,此时可能还需要等操作系统调度,即线程资源的自由竞争。

RUNNABLE(可运行)

  该状态指线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权,转变成为运行中的线程,一个线程处于RUNNABLE状态的意思是在JVM层面它是在执行的,但是该线程可能是在等待操作系统的资源,比如说CPU。注释中说了,它表示线程在JVM层面是执行的,但在操作系统层面不一定,它举例是CPU,毫无疑问CPU是一个操作系统资源,但这也就意味着在等操作系统其他资源的时候,线程也会是这个状态,比如io操作。

BLOCKED(阻塞)

  该状态表示线程在阻塞等待monitor lock(监视器锁)。一个线程在进入synchronized修饰的临界区的时候,或者在synchronized临界区中调用Object.wait然后被唤醒重新进入synchronized临界区都对应该状态。通常是它在等待一个“锁”,当尝试进入一个synchronized语句块/方法时,锁已经被其它线程占有,就会被阻塞,直到另一个线程走完临界区或发生了相应锁对象的wait()操作后,它才有机会去争夺进入临界区的权利。结合上面RUNNABLE的分析,也就是IO阻塞不会进入BLOCKED状态,只有synchronized会导致线程进入该状态。
  关于BLOCKED状态,注释里只提到一种情况就是进入synchronized声明的临界区时会导致,这个也很好理解,synchronized是JVM自己控制的,所以这个阻塞事件它自己能够知道(对比理解上面的操作系统层面)。

WAITING(无限期等待)

  处于这种状态的线程不会被分配CPU执行时间,它们要等待显示的被其它线程唤醒。这种状态通常是指一个线程拥有对象锁后进入到相应的代码区域后,调用相应的“锁对象”的wait()方法操作后产生的一种结果。变相的实现还有LockSupport.park()、Thread.join()等,它们也是在等待另一个事件的发生,也就是描述了等待的意思。
  以下方法会让线程陷入无限期等待状态:
  1.没有设置timeout参数的Object.wait()
  2.没有设置timeout参数的Thread.join()
  3.LockSupport.park()
  处在这个状态的线程是在等另一个线程做一些特殊的操作。比如Object.wait()方法在等另一个线程调用Object.notify()或者Object.notifyAll(),Thread.join()方法在等一个指定线程完成,即变为TERMINATED状态,当然,LockSupport.park则是在等另一个线程调用LockSupport.unpark方法。
  注意:
  LockSupport.park(Object blocker) 会挂起当前线程,参数blocker是用于设置当前线程的“volatile Object parkBlocker 成员变量”
  parkBlocker 是用于记录线程是被谁阻塞的,可以通过LockSupport.getBlocker()获取到阻塞的对象,用于监控和分析线程用的。
  “阻塞”与“等待”的区别:
  1.“阻塞”状态是等待着获取到一个排他锁,进入“阻塞”状态都是被动的,离开“阻塞”状态是因为其它线程释放了锁,不阻塞了;
  2.“等待”状态是在等待一段时间 或者 唤醒动作的发生,进入“等待”状态是主动的
  如主动调用Object.wait(),如无法获取到ReentraantLock,主动调用LockSupport.park(),如主线程主动调用 subThread.join(),让主线程等待子线程执行完毕再执行
离开“等待”状态是因为其它线程发生了唤醒动作或者到达了等待时间

TIMED_WAITING(限期等待)

  处于这种状态的线程也不会被分配CPU执行时间,不过无需等待被其它线程显示的唤醒,在一定时间之后它们会由系统自动的唤醒。
以下方法会让线程进入TIMED_WAITING限期等待状态:
  1.Thread.sleep()方法
  2.设置了timeout参数的Object.wait()方法
  3.设置了timeout参数的Thread.join()方法
  4.LockSupport.parkNanos()方法
  5.LockSupport.parkUntil()方法

TERMINATED(结束)

  已终止线程的线程状态,线程已经结束执行。换句话说,run()方法走完了,线程就处于这种状态。其实这只是Java语言级别的一种状态,在操作系统内部可能已经注销了相应的线程,或者将它复用给其他需要使用线程的请求,而在Java语言级别只是通过Java代码看到的线程状态而已。

线程状态的装换

  通过以上对线程状态的了解,我们知道这几个状态在线程执行过程中是有条件转换的。总结如下图:
thread-trans-state|100x0