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); Listresults = 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 的底层协作模型,对排查并发问题仍有直接帮助。










