ctl高3位存线程池状态(5种状态需3位),低29位存工作线程数,因32−3=29,理论最大线程数为2²⁹−1≈5.0亿,兼顾状态表达与数量精度。

ctl 的位布局怎么拆解:高3位状态 + 低29位线程数
Java 的 ThreadPoolExecutor 用一个 AtomicInteger ctl 同时存两个关键信息:线程池当前运行状态(runState)和当前有效工作线程数(workerCount)。它不是靠对象封装,而是靠位运算硬编码进一个 32 位整数里——高 3 位是状态,低 29 位是数量。
为什么是 3 和 29?因为线程池有 5 种状态(RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED),至少需要 3 个 bit 表示;剩下 29 位留给线程数,理论最大值是 CAPACITY = (1 ,即 536870911,远超实际需求。
常见错误是误以为 ctl 是「状态 + 数量」的简单拼接。其实它是通过移位+按位或组合的:比如 RUNNING 是 -1 (二进制高 3 位全 1),而线程数 3 是 <code>0b11,两者 | 后得到 ctl 值,例如 0b11100000_00000000_00000000_00000011。
runStateOf 和 workerCountOf 怎么从 ctl 中分离出状态和数量
读取时靠两个掩码常量完成无损拆分:
-
runStateOf(c)实际执行c & ~CAPACITY:对ctl按位与上~CAPACITY(即高 3 位为 1、低 29 位为 0 的掩码),只保留高 3 位,其余清零 -
workerCountOf(c)实际执行c & CAPACITY:对ctl按位与上CAPACITY(即高 3 位为 0、低 29 位为 1 的掩码),只保留低 29 位
注意:~CAPACITY 不是简单的取反,而是针对 32 位 int 的补码取反。例如 CAPACITY = 0x1FFFFFFF,则 ~CAPACITY = 0xE0000000,高位正好是状态区。
如果你在调试时打印 ctl.get() 得到一个负数(如 -536870912),别慌——那是 RUNNING 状态(-1 )加 0 个线程的结果,属于正常现象。
ctlOf(rs, wc) 组装时为什么用 | 而不用 +
组装新 ctl 值用的是 rs | wc,不是 rs + wc,原因很实在:
-
rs高 3 位非 0,低 29 位全是 0;wc高 3 位全是 0,低 29 位可能非 0;二者按位或天然互不干扰 - 若用
+,当rs是负数(如RUNNING = -536870912)且wc > 0,会触发符号位参与运算,结果不可控 - 位或操作是幂等、无进位、可预测的,符合原子更新的语义要求
比如 ctlOf(RUNNING, 3) → 0xE0000000 | 0x00000003 = 0xE0000003,干净利落;换成加法就可能溢出或改变状态位。
状态比较为何能直接用 c 这种整数比较
因为所有状态常量都左移了 29 位,彼此之间天然满足大小关系:
RUNNING = -1SHUTDOWN = 0STOP = 1TIDYING = 2TERMINATED = 3
所以 isRunning(c) 直接写成 c ,本质是判断高 3 位是否等于 <code>111(即 -1 的符号扩展形态)。这种设计让状态流转校验极快,也避免了 switch 或枚举比较的开销。
容易被忽略的一点是:状态只能单向推进,但 ctl 本身不阻止你手动构造非法值(比如把 TERMINATED 改回 RUNNING)。线程池靠源码中大量 if (runStateLessThan(c, STOP)) 这类防护逻辑来守住边界,而不是靠 ctl 自身的类型安全。

















