CountDownLatch实现多线程“同一时刻起跑”的核心是让所有线程先调用await()阻塞,待主线程调用countDown()后同时唤醒——计数器初始化为1,不可重用,需确保所有线程在countDown()前完成await()。

Java中用CountDownLatch实现多线程“同一时刻起跑”,核心是让所有线程先阻塞等待,直到主线程发出统一信号后才同时开始执行——这不是真正毫秒级同步,但能高度逼近并发起始点。
理解CountDownLatch的倒计时机制
CountDownLatch是一个一次性同步辅助类,构造时指定一个初始计数值(比如1)。调用countDown()会使计数减1;调用await()的线程会阻塞,直到计数变为0。这个特性天然适合做“发令枪”:所有工作线程在起跑线await(),主线程准备就绪后调用一次countDown(),全员立即放行。
编写起跑控制的典型结构
关键在于分离“就位”和“起跑”两个阶段,避免线程提前执行:
- 主线程创建
CountDownLatch latch = new CountDownLatch(1) - 每个工作线程启动前先调用
latch.await()(放在任务逻辑最开头) - 所有线程启动后,主线程再调用
latch.countDown()——此时所有阻塞线程瞬间恢复运行
完整可运行示例
以下代码演示5个线程模拟“百米起跑”:
立即学习“Java免费学习笔记(深入)”;
public class RaceStart {
public static void main(String[] args) throws InterruptedException {
CountDownLatch startSignal = new CountDownLatch(1);
// 启动5个运动员线程
for (int i = 0; i < 5; i++) {
final int id = i;
new Thread(() -> {
System.out.println("运动员 " + id + " 已就位,等待发令...");
try {
startSignal.await(); // 阻塞在此,直到发令
System.out.println("运动员 " + id + " 起跑!时间:" + System.currentTimeMillis());
// 模拟跑步耗时
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
// 主线程稍作延迟,模拟裁判准备
Thread.sleep(500);
System.out.println("裁判:预备… 跑!");
startSignal.countDown(); // 所有await线程立刻唤醒
}
}
运行时你会看到5条“起跑!”日志几乎同时打印(时间戳差通常在几毫秒内),说明并发起始成功。
注意事项与常见误区
这个方案简单有效,但要注意几点:
-
CountDownLatch不可重用,触发一次后计数归零,后续await()直接返回,如需多次起跑应重建实例 - 确保所有工作线程都在
countDown()前完成await()调用,否则部分线程可能被遗漏;可用额外计数器或CyclicBarrier辅助校验就位状态 - 如果线程在
await()时被中断,会抛出InterruptedException,记得正确处理并恢复中断状态 - 它不保证CPU指令级精确同步,仅提供JVM层面的线程调度协调,对微秒级强一致性场景不适用


















