Java 线程池 ThreadPoolExecutor 的状态控制变量 ctrl
如下是源代码。

线程池的主要控制状态 ctl 是一个原子整数,它打包了两个概念字段:
workerCount:表示当前有效运行的线程数。runState:表示线程池的状态(如是否正在运行、关闭等)。
为了将这两个字段打包成一个 int,我们将 workerCount 限制为 (2^{29} - 1)(约5亿),而不是 (2^{31} - 1)(约20亿)。如果将来这成为一个问题,可以将变量改为 AtomicLong,并调整下面的移位/掩码常量。但在需要出现之前,使用 int 可以使代码更快且更简单。
workerCount 表示被允许启动且未被允许停止的工作线程数量。这个值可能暂时与实际存活线程的数量不同,例如当 ThreadFactory 在被要求创建线程时失败,或者退出线程在终止前仍在进行清理工作时。用户可见的线程池大小报告为 workers 集合的当前大小。
runState 提供主要的生命周期控制,取以下值:
- RUNNING:接受新任务并处理队列中的任务。
- SHUTDOWN:不接受新任务,但处理队列中的任务。
- STOP:不接受新任务,不处理队列中的任务,并中断正在进行的任务。
- TIDYING:所有任务已终止,
workerCount为零,正在转换到TIDYING状态的线程将运行terminated()钩子方法。 - TERMINATED:
terminated()方法已完成。

这些值之间的数值顺序很重要,以便进行有序比较。runState 随时间单调增加,但不一定经过每个状态。状态转换如下:
- RUNNING -> SHUTDOWN:调用
shutdown()时。 - RUNNING 或 SHUTDOWN -> STOP:调用
shutdownNow()时。 - SHUTDOWN -> TIDYING:当队列和线程池都为空时。
- STOP -> TIDYING:当线程池为空时。
- TIDYING -> TERMINATED:当
terminated()钩子方法完成后,等待在awaitTermination()中的线程将在状态达到TERMINATED时返回。
检测从 SHUTDOWN 到 TIDYING 的转换不如预期那样直接,因为队列可能在 SHUTDOWN 状态期间变为空或再次变为非空。我们只能在看到队列为空后,再确认 workerCount 为 0 才能终止(有时需要重新检查)。
关键点总结:
ctl:一个原子整数,包含workerCount和runState。workerCount:表示有效线程数,最大为 (2^{29} - 1)。runState:表示线程池的状态,有多个状态值。- 状态转换:根据调用的方法和条件,线程池状态会从一个状态转换到另一个状态。
- 终止检测:检测从
SHUTDOWN到TIDYING的转换较为复杂,需要确保队列为空且workerCount为 0。
以下是代码的逐行解析,按 变量 和 方法 分类整理为表格:
变量定义表格
| 变量名 | 值/表达式 | 作用 |
|---|---|---|
ctl |
AtomicInteger(ctlOf(RUNNING, 0)) |
原子整数,高3位存储线程池状态(runState),低29位存储工作线程数(workerCount)。初始状态为RUNNING,线程数为0。 |
COUNT_BITS |
Integer.SIZE - 3 |
工作线程数占用的位数(29位),因为32位整数的高3位用于状态。 |
CAPACITY |
(1 << COUNT_BITS) - 1 |
工作线程数的最大值掩码(低29位全1,例如 00011111...),用于从ctl中提取workerCount。 |
RUNNING |
-1 << COUNT_BITS |
运行状态:线程池接受新任务并处理队列中的任务。对应二进制高3位为111。 |
SHUTDOWN |
0 << COUNT_BITS |
关闭状态:不再接受新任务,但处理队列中的剩余任务。高3位为000。 |
STOP |
1 << COUNT_BITS |
停止状态:不再接受新任务,不处理队列任务,并中断进行中的任务。高3位为001。 |
TIDYING |
2 << COUNT_BITS |
整理状态:所有任务已终止,线程数归零,将执行terminated()钩子方法。高3位为010。 |
TERMINATED |
3 << COUNT_BITS |
终止状态:terminated()方法已完成。高3位为011。 |
方法解析表格
| 方法名 | 输入参数 | 返回值/逻辑 | 作用 |
|---|---|---|---|
runStateOf(int c) |
c(ctl的当前值) |
return c & ~CAPACITY; |
从ctl中提取线程池状态(高3位),通过掩码~CAPACITY(高3位为1,低29位为0)过滤。 |
workerCountOf(int c) |
c(ctl的当前值) |
return c & CAPACITY; |
从ctl中提取工作线程数(低29位),通过掩码CAPACITY(低29位为1)过滤。 |
ctlOf(int rs, int wc) |
rs(状态)、wc(线程数) |
return rs | wc; |
将线程池状态(高3位)和工作线程数(低29位)合并为一个整数,形成完整的ctl值。 |
runStateLessThan(int c, int s) |
c(ctl值)、s(目标状态) |
return c < s; |
判断当前状态是否早于目标状态(例如,RUNNING是否在SHUTDOWN之前)。状态值越小,阶段越早。 |
runStateAtLeast(int c, int s) |
c(ctl值)、s(目标状态) |
return c >= s; |
判断当前状态是否等于或晚于目标状态(例如,STOP是否在SHUTDOWN之后)。状态值越大,阶段越晚。 |
isRunning(int c) |
c(ctl值) |
return c < SHUTDOWN; |
判断线程池是否处于运行状态(RUNNING)。因为SHUTDOWN的高3位是000,而其他状态的高3位更大,所以c < SHUTDOWN等价于状态为RUNNING。 |
关键设计思想
-
状态与线程数的合并存储
用一个AtomicInteger(ctl)同时保存线程池状态和工作线程数,通过位操作分离高3位和低29位,减少锁竞争并提高性能。 -
状态顺序与数值关系
状态值按生命周期顺序定义(RUNNING < SHUTDOWN < STOP < TIDYING < TERMINATED),通过直接比较数值大小即可判断状态阶段,避免复杂的逻辑判断。 -
掩码操作
CAPACITY掩码用于提取低29位的线程数(workerCount)。~CAPACITY掩码用于提取高3位的状态(runState)。
-
原子性保证
ctl使用AtomicInteger确保对状态和线程数的修改是原子的,避免多线程环境下的竞态条件。
示例说明
假设ctl的值为 11100000...00000010(高3位为111表示RUNNING,低29位为2):
runStateOf(ctl.get())→RUNNINGworkerCountOf(ctl.get())→2isRunning(ctl.get())→true(因为RUNNING < SHUTDOWN)
通过这种设计,线程池的高效状态管理得以实现。

浙公网安备 33010602011771号