文章目录
一、Java 线程状态
1 NEW(新建)
java.lang.Thread.State枚举中的NEW状态描述:
/**
* Thread state for a thread which has not yet started.
*/
NEW
创建后尚未启动的线程处于这个状态。意思是这个线程没有被 start()
启动,或者说还根本不是一个真正意义上的线程,从本质上讲这只是创建了一个 Java 外壳,还没有真正的线程来运行。不代表调用了 start()
,状态就立即改变,中间还有一些步骤,如果在这个启动的过程中有另一个线程来获取它的状态,其实是不确定的,要看那些中间步骤是否已经完成了
2 RUNNABLE(可运行)
java.lang.Thread.State
枚举中的 RUNNABLE
状态描述:
/**
* 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
RUNNABLE
状态包括了操作系统线程状态中的 Running
和 Ready
,也就是处于此状态的线程可能正在运行,也可能正在等待系统资源,如等待 CPU 为它分配时间片,如等待网络 IO 读取数据。RUNNABLE
状态也可以理解为存活着正在尝试占用 CPU 的线程(有可能这个瞬间并没有占用 CPU,但是它可能正在发送指令等待系统调度)。由于在真正的系统中,并不是开启一个线程后,CPU 就只为这一个线程服务,它必须使用许多调度算法来达到某种平衡,不过这个时候线程依然处于 RUNNABLE
状态
3 BLOCKED(阻塞)
java.lang.Thread.State
枚举中的 BLOCKED
状态描述:
/**
* 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
BLOCKED
称为阻塞状态,或者说线程已经被挂起,它睡着了,原因通常是它在等待一个锁,当尝试进入一个 synchronized
语句块/方法时,锁已经被其它线程占有,就会被阻塞,直到另一个线程走完临界区或发生了相应锁对象的wait()操作后,它才有机会去争夺进入临界区的权利
在 Java 代码中,需要考虑 synchronized
的粒度问题,否则一个线程长时间占用锁,其它争抢锁的线程会一直阻塞,直到拥有锁的线程释放锁
处于 BLOCKED
状态的线程,即使对其调用 thread.interrupt()
也无法改变其阻塞状态,因为 interrupt()
方法只是设置线程的中断状态,即做一个标记,不能唤醒处于阻塞状态的线程
注意:
ReentrantLock.lock()
操作后进入的是WAITING
状态,其内部调用的是LockSupport.park()
方法
4 WAITING(无限期等待)
/**
* 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
处于这种状态的线程不会被分配 CPU 执行时间,它们会一直等直到被其它线程唤醒
以下方法会让线程陷入无限期等待状态:
- 没有设置
timeout
参数的Object.wait()
- 没有设置
timeout
参数的Thread.join()
LockSupport.park()
LockSupport.park(Object blocker)
会挂起当前线程,参数blocker
是用于设置当前线程的 “volatile Object parkBlocker
” 成员变量
parkBlocker
是用于记录线程是被谁阻塞的,可以通过LockSupport.getBlocker()
获取到阻塞的对象,用于监控和分析线程用的
BLOCKED
与 WAITING
的区别:
BLOCKED
状态是等待获取到一个排他锁,进入BLOCKED
状态都是被动的,离开BLOCKED
状态是因为其它线程释放了锁,而该阻塞线程得到了锁WAITING
状态是在等待一段时间(timeout)或者其他线程唤醒动作的发生,进入WAITING
状态是主动的
5 TIMED_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
处于这种状态的线程也不会被分配 CPU 执行时间,不过无需等待被其它线程显示的唤醒,在一定时间之后它们会由系统自动的唤醒
以下方法会让线程进入TIMED_WAITING
限期等待状态:
- Thread.sleep()
- 设置了timeout参数的Object.wait()
- 设置了timeout参数的Thread.join()
- LockSupport.parkNanos()
- LockSupport.parkUntil()
6 TERMINATED(结束)
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED
已终止线程的线程状态,线程已经结束执行。换句话说,run() 方法执行完成,线程便处于这种状态。其实这只是 Java 语言级别的一种状态,在操作系统内部可能已经注销了相应的线程,或者将它复用给其他需要使用线程的请求
二、Java 线程状态转换图
三、VisualVM 线程监控线程状态与 Java 线程状态对应关系
通过 VisualVM 监控 JVM 时,可以通过线程标签页查看JVM的线程信息,VisualVM 的线程状态如下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-55Hec1r9-1600098042778)(D:\work\公共服务\java\并发编程\images\677054-20170325171859830-198146769.jpg)]
通过 dump thread stack,并与 VisualVM 监控信息中的线程名称对应,找到的 VisualVM 每种线程状态的线程堆栈如下:
1 运行
"http-bio-8080-Acceptor-0" daemon prio=6 tid=0x000000000d7b4800 nid=0xa264 runnable [0x000000001197e000]
java.lang.Thre