Java中线程有六种状态:
其中,Timed Waiting就是带超时时间的Waiting,二者比较类似,因此这里只讨论Waiting状态。
各种状态之间的转化关系如下:
新创建的线程,还未调用Thread.start()之前,都处于"New"状态。
public class ThreadStateTest_New {
public static void main(String[] args) {
Thread thread = new Thread();
// NEW
System.out.println(thread.getState().name());
}
}
Runnable表示可以执行的线程,它其实可以看做是以下两种状态的一个统称:
Running表示正在CPU上执行的线程
Ready表示从JVM的角度来看,是一个可以执行的线程,但是由于操作系统的资源限制而导致当前无法执行。 操作系统的资源限制可能有下面两种情况:
比如,4核的机器上启动了8个线程,那么在某一时刻肯定会有一些线程无法在CPU上执行,但是它们依然是Runnable的。CPU到底会执行哪一个线程由操作系统的调度程序来决定。
测试代码:
public class ThreadStateTest_Runnable_1 {
public static void main(String[] args) throws Exception {
// 当前电脑4核,但是测试结果显示5个线程都处于Runnable状态。
for (int i = 0; i < Runtime.getRuntime().availableProcessors() + 1; i++) {
new Thread(new RunningOrRunnableThread(), "RunningOrRunnableThread_" + i).start();
}
Thread.sleep(Integer.MAX_VALUE);
}
static class RunningOrRunnableThread implements Runnable {
@Override
public void run() {
int tot = 0;
while (true) {
tot = tot + 1;
}
}
}
}
jstack结果:
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.181-b13 mixed mode):
"RunningOrRunnableThread_4" #17 prio=5 os_prio=0 tid=0x0000000019556800 nid=0x61c4 runnable [0x000000001a68f000]
java.lang.Thread.State: RUNNABLE
at java并发编程的艺术.ch4_1.线程状态.ThreadStateTest_Runnable_1$RunningOrRunnableThread.run(ThreadStateTest_Runnable_1.java:27)
at java.lang.Thread.run(Thread.java:748)
"RunningOrRunnableThread_3" #16 prio=5 os_prio=0 tid=0x0000000019555800 nid=0x517c runnable [0x000000001a58f000]
java.lang.Thread.State: RUNNABLE
at java并发编程的艺术.ch4_1.线程状态.ThreadStateTest_Runnable_1$RunningOrRunnableThread.run(ThreadStateTest_Runnable_1.java:27)
at java.lang.Thread.run(Thread.java:748)
"RunningOrRunnableThread_2" #15 prio=5 os_prio=0 tid=0x0000000019554000 nid=0x632c runnable [0x000000001a48e000]
java.lang.Thread.State: RUNNABLE
at java并发编程的艺术.ch4_1.线程状态.ThreadStateTest_Runnable_1$RunningOrRunnableThread.run(ThreadStateTest_Runnable_1.java:27)
at java.lang.Thread.run(Thread.java:748)
"RunningOrRunnableThread_1" #14 prio=5 os_prio=0 tid=0x0000000019551000 nid=0x3338 runnable [0x000000001a38e000]
java.lang.Thread.State: RUNNABLE
at java并发编程的艺术.ch4_1.??程状态.ThreadStateTest_Runnable_1$RunningOrRunnableThread.run(ThreadStateTest_Runnable_1.java:27)
at java.lang.Thread.run(Thread.java:748)
"RunningOrRunnableThread_0" #13 prio=5 os_prio=0 tid=0x0000000019550800 nid=0x61b4 runnable [0x000000001a28e000]
java.lang.Thread.State: RUNNABLE
at java并发编程的艺术.ch4_1.线程状态.ThreadStateTest_Runnable_1$RunningOrRunnableThread.run(ThreadStateTest_Runnable_1.java:27)
at java.lang.Thread.run(Thread.java:748)
比如,一个线程阻塞在InputStream.read(…)方法上,等待着某种输入。此时,该线程也是Runnable的。
测试代码:
public class ThreadStateTest_Runnable_2 {
public static void main(String[] args) throws Exception {
new Thread(new BlockIOThread(), "BlockIOThread").start();
Thread.sleep(Integer.MAX_VALUE);
}
static class BlockIOThread implements Runnable {
@Override
public void run() {
Scanner scanner = new Scanner(System.in);
String line = scanner.nextLine(); // 阻塞在InputStream.read()方法上
}
}
}
jstack结果:
"BlockIOThread" #13 prio=5 os_prio=0 tid=0x0000000018d8f000 nid=0x463c runnable [0x0000000019b0e000]
java.lang.Thread.State: RUNNABLE
at java.io.FileInputStream.readBytes(Native Method)
at java.io.FileInputStream.read(FileInputStream.java:255)
at java.io.BufferedInputStream.read1(BufferedInputStream.java:284)
at java.io.BufferedInputStream.read(BufferedInputStream.java:345)
- locked <0x00000000d5ddad88> (a java.io.BufferedInputStream)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
- locked <0x00000000d60e2340> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.Reader.read(Reader.java:100)
at java.util.Scanner.readInput(Scanner.java:804)
at java.util.Scanner.findWithinHorizon(Scanner.java:1685)
at java.util.Scanner.nextLine(Scanner.java:1538)
at java并发编程的艺术.ch4_1.线程状态.ThreadStateTest_Runnable_2$BlockIOThread.run(ThreadStateTest_Runnable_2.java:23)
线程只有一种情况会进入Blocked状态 —— 当线程尝试进入一个synchronized代码块,但是内部的监视器锁正在被其他线程所占有时,它就会进入Blocked状态。 … 当线程成功获取到监视器锁后,它又会进入Runnable状态。
测试代码:
public class ThreadStateTest_Blocked {
public static void main(String[] args) throws Exception {
new Thread(new BlockedThread(), "BlockedThread_1").start();
Thread.sleep(2000); // 睡两秒,让第一个线程有充分的时间去获取锁。
new Thread(new BlockedThread(), "BlockedThread_2").start();
Thread.sleep(Integer.MAX_VALUE);
}
static Object obj = new Object();
static class BlockedThread implements Runnable {
@Override
public void run() {
synchronized (obj) {
// 获取锁后死循环, 不释放
while (true) {
}
}
}
}
}
jstack结果:
"BlockedThread_2" #14 prio=5 os_prio=0 tid=0x000000001910c800 nid=0x1bb8 waiting for monitor entry [0x0000000019f3f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at java并发编程的艺术.ch4_1.线程状态.ThreadStateTest_Blocked$BlockedThread.run(ThreadStateTest_Blocked.java:27)
- waiting to lock <0x00000000d6032ee8> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
"BlockedThread_1" #13 prio=5 os_prio=0 tid=0x000000001901f000 nid=0x2150 runnable [0x0000000019e3e000]
java.lang.Thread.State: RUNNABLE
at java并发编程的艺术.ch4_1.线程状态.ThreadStateTest_Blocked$BlockedThread.run(ThreadStateTest_Blocked.java:27)
- locked <0x00000000d6032ee8> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
Object中的 wait() 和 notify() / notifyAll()的使用方法如下:
// 等待方
synchronized(obj) {
while (条件不满足) {
obj.wait();
}
对应的处理逻辑。
}
// 唤醒方:
synchronized(obj) {
改变条件
obj.notifyAll();
}
当线程调用Object.wait()方法后,会进入Waiting状态,并释放掉它已经拥有的监视器锁。 当它被其他线程通过 notify/notifyAll 唤醒之后,它要做的第一件事就是要重新获取监视器锁,因此它会进入Blocked状态,直到成功获取锁以后才能进入Runnable状态。
Thread.join()方法也是通过Object.wait()来实现的。
测试代码:
public class ThreadStateTest_Waiting_1 {
public static void main(String[] args) throws Exception {
new Thread(new WaitingThread(), "WaitingThread_1").start();
Thread.sleep(Integer.MAX_VALUE);
}
static Object obj = new Object();
static class WaitingThread implements Runnable {
@Override
public void run() {
try {
synchronized (obj) {
obj.wait();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
jstack结果:
"WaitingThread_1" #13 prio=5 os_prio=0 tid=0x00000000194ce800 nid=0x259c in Object.wait() [0x000000001a20e000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000d6032b68> (a java.lang.Object)
at java.lang.Object.wait(Object.java:502)
at java并发编程的艺术.ch4_1.线程状态.ThreadStateTest_Waiting_1$WaitingThread.run(ThreadStateTest_Waiting_1.java:24)
- locked <0x00000000d6032b68> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
测试代码2——wait被唤醒之后进入blocked状态:
public class ThreadStateTest_Waiting_2 {
public static void main(String[] args) throws Exception {
new Thread(new WaitingThread(), "WaitingThread_1").start();
synchronized (obj) {
// main线程能够获取到锁,说明 WaitingThread 一定已经调用了wait()方法
obj.notify();
// 唤醒 WaitingThread, 但是永远不释放锁。
while (true) {
}
}
}
static Object obj = new Object();
/**
* java.lang.Thread.State: BLOCKED (on object monitor)
*/
static class WaitingThread implements Runnable {
@Override
public void run() {
try {
synchronized (obj) {
obj.wait();
// 从waiting状态中被notify()唤醒以后,线程进入BLOCKED状态,只有重新获取到锁之后才能进入RUNNABLE状态。
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
jstack结果2: 在代码的第33行的位置上Blocked,即obj.wait()这一行代码。
"WaitingThread_1" #13 prio=5 os_prio=0 tid=0x00000000189b8000 nid=0x4f2c waiting for monitor entry [0x000000001973f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at java并发编程的艺术.ch4_1.线程状态.ThreadStateTest_Waiting_2$WaitingThread.run(ThreadStateTest_Waiting_2.java:33)
- waiting to lock <0x00000000d6033020> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
调用LockSupport.park()方法后,线程也会进入Waiting状态。其他线程可以通过LockSupport.unpark(Thread)唤醒该等待线程,被唤醒后它会进入到Runnable状态。
java.util.concurrent包中的很多同步工具类(ReentrantLock、CountDownLatch等)都是通过AQS(AbstractQueuedSynchronizer)来实现的,而AQS又是通过LockSupport.park来实现等待和唤醒的,因此我们可以用ReentrantLock来测试这一种情况。
测试代码:
public class ThreadStateTest_Waiting_3 {
public static void main(String[] args) throws Exception {
new Thread(new WaitingThread(), "WaitingThread_1").start();
Thread.sleep(2000); // 睡两秒,让第一个线程有充分的时间去获取锁。
new Thread(new WaitingThread(), "WaitingThread_2").start();
Thread.sleep(Integer.MAX_VALUE);
}
static Lock lock = new ReentrantLock();
static class WaitingThread implements Runnable {
@Override
public void run() {
lock.lock(); // 尝试获取锁。
}
}
}
jstack结果:
"WaitingThread_2" #14 prio=5 os_prio=0 tid=0x00000000197cd800 nid=0x405c waiting on condition [0x000000001a52f000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000d603d8e8> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
at java并发编程的艺术.ch4_1.线程状态.ThreadStateTest_Waiting_3$WaitingThread.run(ThreadStateTest_Waiting_3.java:23)
at java.lang.Thread.run(Thread.java:748)
终止状态,可能有两种情况: