CountDownLatch 是解决主线程等待多子任务完成再汇总的直接方案,需配合线程池、原子变量及异常兜底;初始化值等于任务数,主线程调用 await(),各子线程在 finally 中调用 countDown(),确保计数归零放行。

CountDownLatch 是解决“主线程等待多个子任务完成后再汇总结果”这类问题的直接方案。它不负责任务调度,也不管理线程安全的变量操作,而是专注做一件事:计数归零即放行。要真正落地多任务并行计算+最终汇总,需配合线程池、原子变量和规范的异常兜底。
核心逻辑不能错:谁创建、谁 await、谁 countDown
CountDownLatch 实例必须被所有参与线程共享——主线程调用 await() 阻塞,每个子任务在线程结束(无论成功或失败)时必须调用 countDown()。漏调一次,主线程就永远卡住。
- 初始化值等于子任务总数,例如启动 5 个统计任务,就 new CountDownLatch(5)
- 主线程在提交全部任务后立即调用 latch.await(),不加锁、不重试、不跳过
- 每个子任务的执行逻辑末尾(推荐放在 finally 块中)执行 latch.countDown()
变量汇总必须线程安全:别用普通 int/ArrayList
多个子线程并发写同一个变量,普通类型会丢失更新。比如 10 个线程各加 1,最后结果可能远小于 10。
- 求和类场景:用 AtomicInteger 或 AtomicLong 替代 int/long
- 收集列表类场景:用 ConcurrentLinkedQueue 或 CopyOnWriteArrayList,或让每个线程返回局部 List,主线程再合并
- 避免在子线程中直接修改主线程的 ArrayList 或 HashMap —— 没有同步机制就是隐患
异常处理是生产级关键:不能因一个失败导致整体挂起
子任务抛出未捕获异常时,线程会终止,但若没走到 countDown(),计数器就无法归零。这是线上最常见的死锁诱因之一。
- 务必把 countDown() 放在 try-catch-finally 的 finally 块里
- 记录异常日志,但不阻止 countDown 执行
- 如需区分成功/失败任务,可额外用 ConcurrentHashMap 记录每个任务状态,而非依赖 countDown 是否发生
配合线程池使用才真正高效
单独 new Thread 启动任务开销大且难管理。生产环境应搭配固定大小线程池,控制资源消耗。
- 用 Executors.newFixedThreadPool(n) 或自定义 ThreadPoolExecutor
- 任务提交统一用 executor.submit(Runnable),避免手动 start
- 线程池 + CountDownLatch 组合后,主线程只管 await,其余全由池管理,结构清晰、可监控、易扩容

















