countdownlatch 是一个一次性同步辅助类,用于让一个或多个线程等待其他线程完成指定数量的操作;适用于主线程等待多个子任务(如批量 http 请求)全部完成后再汇总结果的场景。

CountDownLatch 是什么,什么时候该用它
CountDownLatch 不是锁,而是一个同步辅助类,用来让一个或多个线程等待其他线程完成一组操作。典型场景是:主线程启动若干子任务(比如 5 个 HTTP 请求),等它们全部返回后再汇总结果——这时 CountDownLatch 比轮询或 join() 更可控、更轻量。
它内部维护一个计数器,初始化时指定值;每次调用 countDown() 就减 1;调用 await() 的线程会阻塞,直到计数器归零或被中断。
初始化和基本用法要注意哪些坑
最常见错误是把 CountDownLatch 初始化为 0:此时 await() 不会阻塞,直接返回,导致逻辑提前执行。另一个坑是重复调用 countDown() 超过初始值,虽然不会报错,但可能掩盖逻辑错误(比如本该只触发一次的清理动作被多执行)。
- 计数值必须大于 0 才有意义,
new CountDownLatch(0)适合“立即放行”场景,但需明确意图 - 每个参与协作的线程应且仅调用一次
countDown(),建议放在finally块里防异常跳过 -
await()可带超时参数,避免无限等待;返回false表示超时,需主动处理失败路径
和 CyclicBarrier、Semaphore 的关键区别在哪
三者都用于线程协作,但语义不同:CyclicBarrier 是“大家一起到齐才出发”,可重用;Semaphore 控制并发数,类似限流器;而 CountDownLatch 是“等别人干完我再动”,一次性且不可重置。
立即学习“Java免费学习笔记(深入)”;
如果需要多次等待同一组事件(比如每轮批处理都等 10 个任务完成),别硬用 CountDownLatch —— 它不能 reset,得换 CyclicBarrier 或手动 new 新实例(不推荐,易泄漏)。
-
CountDownLatch:单次倒计时,不可重用 -
CyclicBarrier:可重复使用,支持到达时触发回调(Runnable) -
Semaphore:控制同时访问资源的线程数,和“等待完成”无直接关系
一个真实可用的 HTTP 批量请求示例
下面代码模拟 3 个异步请求并发执行,主线程等待全部完成。注意异常处理、超时控制和资源释放:
CountDownLatch latch = new CountDownLatch(3);
List<String> results = Collections.synchronizedList(new ArrayList<>());
for (int i = 0; i < 3; i++) {
int taskId = i;
new Thread(() -> {
try {
// 模拟网络请求
String result = "response-" + taskId;
Thread.sleep(1000L);
results.add(result);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
latch.countDown(); // 确保一定执行
}
}).start();
}
try {
if (!latch.await(5, TimeUnit.SECONDS)) {
System.err.println("Timeout waiting for all tasks");
return;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
System.out.println("All done: " + results);
实际项目中,更推荐用 CompletableFuture 替代手写 CountDownLatch,尤其在需要链式处理、异常传播或组合多个异步结果时。但理解 CountDownLatch 的底层协作模型,对排查并发问题仍有直接帮助。









