addworker方法不使用双重检查锁,而是采用双重循环+cas+mainlock机制:外层循环校验线程池状态,内层循环通过cas竞争增加线程数,成功后才加锁操作workers集合。

ThreadPoolExecutor 中的 addWorker 方法**并不使用双重检查锁(Double-Checked Locking)**,它用的是**双重循环 + CAS + 全局锁(mainLock)** 的组合机制,目的不是为了单例初始化,而是为了**线程安全地判断线程池状态并原子性地增加工作线程数**。
为什么不是双重检查锁?
双重检查锁是典型的单例模式优化技巧,依赖 volatile + synchronized + 两次 null 判断。而 addWorker:
- 没有 volatile 引用需要保护;
- 没有“懒汉式初始化一个共享实例”的语义;
- 不靠 synchronized 块做临界区保护(它在关键路径上用的是 mainLock.lock(),且 CAS 操作本身无锁);
- 它的“双重”体现在两个 for 循环嵌套,用于反复校验线程池运行状态和线程计数器(ctl),不是为了解决多线程下初始化竞态。
addWorker 的双重循环真实作用
外层循环负责「状态兜底」:持续读取 ctl,判断线程池是否仍处于可接受新线程的状态(如 RUNNING 或 SHUTDOWN 且队列非空)。
内层循环负责「计数器竞争」:通过 CAS 尝试将线程数字段(ctl 的低 29 位)+1;若失败(被其他线程抢先修改),就重试,直到成功或发现状态已不可用。
这种设计避免了长期持有锁,把高并发下的“状态判断 + 计数变更”做成无锁+重试,兼顾性能与一致性。
真正加锁的地方在后面
CAS 成功后,addWorker 才会获取 mainLock(ReentrantLock),把新创建的 Worker 对象加入 workers 集合,并启动其内部线程:
- mainLock 保证对 workers HashSet 的操作线程安全;
- Worker 启动后调用 runWorker,此时才真正开始执行任务;
- 整个流程中,锁只在集合操作和资源注册阶段出现,不是贯穿始终的同步块。
容易混淆的点:和 execute() 里的“双重检查”不同
execute() 方法中确实有类似“双重检查”的逻辑:先尝试加队列,再 recheck 线程池状态防止关闭期间任务漏处理。但那是针对任务入队后的状态二次确认,和 addWorker 的循环结构无关。
addWorker 的核心挑战是——在不停止服务的前提下,安全扩容线程数。它靠的是状态机驱动 + 原子计数 + 有限范围加锁,不是 DCL 模式。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











