线程池状态小总结
摘要
根据 ThreadPoolExecutor.java 中的描述,线程池共有如下 5 种状态 ⬇️
- RUNNING
- SHUTDOWN
- STOP
- TIDYING
- TERMINATED
它们之间的转化关系如下 ⬇️
stateDiagram-v2
RUNNING --> SHUTDOWN: On invocation of shutdown()
SHUTDOWN --> STOP: On invocation of shutdownNow()
RUNNING --> STOP: On invocation of shutdownNow()
SHUTDOWN --> TIDYING: When both queue and pool are empty
STOP --> TIDYING: When pool is empty
TIDYING --> TERMINATED: When the terminated() hook method has completed
我们在本文中验证一下这些状态之间的转化关系。
正文
根据 ThreadPoolExecutor.java 中的描述,线程池共有以下 5 种状态 ⬇️
| 状态 | 解释 |
|---|---|
| RUNNING | Accept new tasks and process queued tasks |
| SHUTDOWN | Don't accept new tasks, but process queued tasks |
| STOP | Don't accept new tasks, don't process queued tasks, and interrupt in-progress tasks |
| TIDYING | All tasks have terminated, workerCount is zero, the thread transitioning to state TIDYING will run the terminated() hook method |
| TERMINATED | terminated() has completed |
ThreadPoolExecutor.java 里描述了这 5 种状态之间的转化条件 ⬇️
按照上图的描述,我画了下图 ⬇️ (我按照自己的理解在图中也画了中文的版本,如需要查看严谨的描述,请参考英文版本的)
stateDiagram-v2
state original {
RUNNING --> SHUTDOWN: On invocation of shutdown()
SHUTDOWN --> STOP: On invocation of shutdownNow()
RUNNING --> STOP: On invocation of shutdownNow()
SHUTDOWN --> TIDYING: When both queue and pool are empty
STOP --> TIDYING: When pool is empty
TIDYING --> TERMINATED: When the terminated() hook method has completed
}
state 中文版 {
state "RUNNING" as r
state "SHUTDOWN" as sh
state "STOP" as st
state "TIDYING" as ti
state "TERMINATED" as te
r --> sh: 调用 shutdown() 方法时
sh --> st: 调用 shutdownNow() 方法时
r --> st: 调用 shutdownNow() 方法时
sh --> ti: 当 workQueue 字段和 workers 字段都\n变成空(即,size 为 0)时
st --> ti: 当 workers 字段变成空(即,size 为 0)时
ti --> te: 当 terminated() 方法调用结束时
}
准备工作
在 ThreadPoolExecutor.java 里可以找到如下的代码 ⬇️
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int COUNT_MASK = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
从这些代码(以及 ctl 字段 的注释)可以看出 ctl 里的 value 字段的高 3 位(因为 COUNT_BITS 的值是 29)保存了线程池的状态信息。
| 状态 | 值 | 用十进制展示 |
|---|---|---|
| RUNNING | -1 << 29 | |
| SHUTDOWN | 0 << 29 | |
| STOP | 1 << 29 | |
| TIDYING | 2 << 29 | |
| TERMINATED | 3 << 29 |
查看 RUNNING 状态(其对应值为 -1 << 29 即 )
请将以下代码保存为 CheckRunning.java ⬇️
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CheckRunning {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(() -> System.out.println("Hello, World"));
executorService.shutdown();
}
}
借助 Intellij IDEA (Community Edition) 可以在第 7 行打断点,然后输入以下内容就可以验证线程池此时的状态就是 RUNNING(因为下面这个表达式的值是 ,和 RUNNING 对应的值一样)
ThreadPoolExecutor.runStateOf(((ThreadPoolExecutor) executorService).ctl.get())
查看 SHUTDOWN 状态(其对应值为 )
在线程池处于 RUNNING 状态时,如果我们对它调用 shundown() 方法,那么它会变为 SHUTDOWN 状态。但考虑到这个状态可能只会持续很短的时间,所以我写了如下的代码,以使得 SHUTDOWN 状态一直持续下去 ⬇️
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CheckShutdown {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
// 下一行代码提交了一个会一直运行的 `Runnable` 对象,所以对线程池调用 shutdown() 方法后,线程池不会从 `SHUTDOWN` 状态变为 `TIDYING` 状态
executorService.execute(() -> {
while (true) {
Thread.yield();
}
});
executorService.shutdown();
int a = 42; // 这一行的代码没有实际的意义,只是为了方便打断点才写了这一行
}
}
请将以上代码保存为 CheckShutdown.java。
借助 Intellij IDEA (Community Edition) 可以在第 14 行打断点,然后输入以下内容就可以验证线程池此时的状态就是 SHUTDOWN(因为下面这个表达式的值是 ,和 SHUTDOWN 对应的值一样)
ThreadPoolExecutor.runStateOf(((ThreadPoolExecutor) executorService).ctl.get())
查看 STOP 状态(其对应值为 1 << 29 即 )
请将以下代码保存为 CheckStop.java ⬇️
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CheckStop {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.shutdownNow();
}
}
我们可以在 ThreadPoolExecutor.java 的 shutdownNow() 方法里找个合适的位置来打断点,例如下图这个位置 ⬇️
当运行到断点处,输入以下内容就可以验证线程池此时的状态就是 STOP(因为下面这个表达式的值是 ,和 STOP 对应的值一样)
ThreadPoolExecutor.runStateOf(this.ctl.get())
查看 TIDYING 状态(其对应值为 2 << 29 即 )
由前文展示的状态图可知,当线程池从 TIDYING 状态转化为 TERMINATED 状态的条件是 terminated() 方法执行完。在 ThreadPoolExecutor.java 里,terminated() 的代码是这样的 ⬇️
/**
* Method invoked when the Executor has terminated. Default
* implementation does nothing. Note: To properly nest multiple
* overridings, subclasses should generally invoke
* {@code super.terminated} within this method.
*/
protected void terminated() { }
由于 terminated() 里没有任何逻辑,所以当线程池处于 TIDYING 状态时,会立刻转化为 TERMINATED 状态。这样就很难观察到 TIDYING 状态。但也有变通的办法,既然上面这个 terminated() 方法里什么代码都没有,那我们可以定一个 ThreadPoolExecutor 的子类,并在子类中 override terminated() 方法,这样在子类的 terminated() 方法里,就可以打断点进行观察了。具体的代码如下 ⬇️ (请将代码保存为 CheckTidying.java)
import java.util.concurrent.*;
public class CheckTidying {
public static void main(String[] args) {
ExecutorService executorService = new MyThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<>());
executorService.shutdown();
}
}
class MyThreadPoolExecutor extends ThreadPoolExecutor {
public MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
@Override
protected void terminated() {
super.terminated();
}
}
借助 Intellij IDEA (Community Edition) 可以在第 20 行打断点,然后输入以下内容就可以验证线程池此时的状态就是 TIDYING(因为下面这个表达式的值是 ,和 TIDYING 对应的值一样)
ThreadPoolExecutor.runStateOf(this.ctl.get())
查看 TERMINATED 状态(其对应值为 3 << 29 即 )
请将以下代码保存为 CheckTerminated.java ⬇️
import java.util.concurrent.*;
public class CheckTerminated {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(() -> System.out.println("Hello world"));
executorService.shutdown();
while (!executorService.awaitTermination(10, TimeUnit.SECONDS)) {
System.out.println("I am waiting");
}
int a = 42; // 这一行的代码没有实际的意义,只是为了方便打断点才写了这一行
}
}
借助 Intellij IDEA (Community Edition) 可以在第 11 行打断点,然后输入以下内容就可以验证线程池此时的状态就是 TERMINATED(因为下面这个表达式的值是 ,和 TERMINATED 对应的值一样)
ThreadPoolExecutor.runStateOf(((ThreadPoolExecutor) executorService).ctl.get())