CountDownLatch 是 Java 并发包中用于线程协调的轻量级同步工具,核心是通过一个一次性计数器(如 CountDownLatch(1))实现“并发起跑”:所有线程调用 await() 阻塞等待,主线程调用 countDown() 后全部唤醒执行。

CountDownLatch 是 Java 并发包(java.util.concurrent)中一个轻量级的同步工具,常用于让一个或多个线程等待,直到其他线程完成一组操作。它特别适合“协调多个线程同时启动”这一场景——也就是常说的“并发起跑”(race start)。
核心思路:用 CountDownLatch 控制“起跑信号”
关键在于:所有待启动的线程先调用 latch.await() 阻塞自己;主线程(或协调线程)在准备好后调用 latch.countDown() 一次,所有等待线程瞬间被唤醒并开始执行。
注意:CountDownLatch 是**一次性**的,计数器归零后无法重置,需重新创建新实例。
典型用法:主线程发令,工作线程集体出发
下面是一个清晰、可运行的示例:
OpenJDK 24 Windows x64 官方参考实现(24+36),适合 Windows 环境进行 Java 开发、编译、运行和版本兼容性验证。
立即学习“Java免费学习笔记(深入)”;
CountDownLatch startSignal = new CountDownLatch(1); // 起跑信号,1 表示等 1 次 countDown
CountDownLatch doneSignal = new CountDownLatch(n); // 完成信号,等 n 个线程都 finish
for (int i = 0; i < n; i++) {
new Thread(() -> {
try {
startSignal.await(); // 所有线程在此处阻塞,等待发令
// ✅ 此处才是真正的业务逻辑开始点(真正“启动”)
doWork();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
doneSignal.countDown(); // 标记自己已完成
}
}).start();
}
// 主线程:一切就绪后,发出起跑信号
Thread.sleep(100); // 模拟准备时间(如加载配置、初始化资源)
startSignal.countDown(); // ? 关键一步:所有 await 的线程立即解除阻塞
// 可选:等待全部完成
doneSignal.await();
常见误区与注意事项
-
别把 startSignal 设为 n:如果设成
new CountDownLatch(n),就要调用 n 次countDown()才能唤醒,这不符合“统一发令”的意图;起跑信号应为 1。 -
await() 可能被中断:务必捕获
InterruptedException并正确处理(推荐恢复中断状态:Thread.currentThread().interrupt())。 -
不要依赖 sleep 代替 await:用
Thread.sleep()模拟同步既不精确也不可靠,容易因调度延迟导致“假并发”或竞争问题。 -
doneSignal 是可选的:如果只关心“同时启动”,不关心执行结果,可以省略
doneSignal;若需统计完成情况或做后续汇总,则保留它。
替代方案对比(为什么不用 wait/notify 或 synchronized?)
虽然也能用 Object.wait()/notifyAll() 实现类似效果,但 CountDownLatch 更安全、简洁:
- 无需手动管理锁对象和同步块,避免死锁或 notify 遗漏风险;
- 线程安全,内部基于 AQS 实现,无额外同步开销;
- 语义明确:“等待计数归零”比“等待某个条件成立”更贴合“发令启动”的场景。
不复杂但容易忽略:起跑信号的 CountDownLatch(1) + countDown() 是整个协调机制的支点,只要抓住这点,多线程齐步启动就非常可控。

















