volatile保证状态可见性但不阻塞线程,CountDownLatch控制线程时序并阻塞等待;二者可共存协同:volatile作状态标记,CountDownLatch作同步门闩,不可互相替代或错误混用。

volatile 和 CountDownLatch 解决的是不同层面的同步问题,它们不直接“配合”使用,但可以共存于同一场景中,各自承担明确职责:volatile 保证状态变量的可见性,CountDownLatch 控制线程执行时序。混淆二者作用,容易误用。
volatile 负责“看到最新值”
它确保一个线程对变量的修改,能被其他线程立即感知。但它不提供原子性,也不阻塞线程。
- 适合做开关标志、就绪信号、终止指令等简单布尔或数值状态
- 例如:
private volatile boolean ready = false;,线程 A 设置ready = true后,线程 B 在 while 循环中读取该值不会因缓存旧值而卡住 - 不能用于
count++这类复合操作——即使加了 volatile,仍可能丢失更新
CountDownLatch 负责“等够人数再发车”
它是一个一次性门闩,通过内部计数器协调多个线程的启动或结束时机。调用 await() 的线程会阻塞,直到计数器归零;countDown() 则递减计数器。
- 典型用途:主线程等待多个子任务完成后再汇总结果
- 它的线程安全由 JDK 内部保证,无需额外加锁或 volatile
- 一旦计数器为 0,所有 await 中的线程被唤醒,后续 await() 将立即返回
何时一起出现?各干各的活
常见组合模式是:用 volatile 标记某个中间状态(如“预热完成”),用 CountDownLatch 控制最终就绪信号。二者不嵌套、不依赖,只是逻辑上协同。
立即学习“Java免费学习笔记(深入)”;
- 比如启动服务时,多个初始化模块并行执行,每个模块完成后设置自己的 volatile 标志;同时有一个 CountDownLatch(3) 等待全部模块调用
countDown() - 主线程不轮询 volatile 变量,而是调用
latch.await()—— 更高效、不空转、响应及时 - volatile 可作为调试辅助:日志中打印
module1Ready、module2Ready,帮助定位哪个模块卡住,但不参与同步逻辑
别踩的坑
以下写法看似“加强同步”,实则多余甚至有害:
- 把 CountDownLatch 对象声明为 volatile —— 没意义,latch 本身不可变,且其内部状态已线程安全
- 用 volatile int 替代 CountDownLatch 实现倒计时 —— 无法阻塞等待,必须手动 while + sleep,浪费 CPU,还易出竞态
- 在
await()前检查 volatile 变量决定是否等待 —— 破坏 latch 的原子协调语义,引入竞态窗口


















