CountDownLatch 与线程池协同实现多源并行抓取与聚合:线程池负责并发执行,CountDownLatch 精准阻塞主线程等待全部任务完成(无论成功或失败),必须在 finally 中调用 countDown() 并设置 await 超时以保障可靠性。

线程池配合 CountDownLatch 是实现多源数据并行抓取与聚合的成熟方案:线程池负责并发执行多个数据源请求,CountDownLatch 负责让主线程精准等待全部子任务完成,再统一聚合结果。关键不在“能不能跑”,而在“等得准、收得全、出错可感知”。
明确分工:线程池管执行,CountDownLatch 管等待
线程池不负责同步逻辑,只专注调度和复用线程;CountDownLatch 不参与任务执行,只提供计数+阻塞+唤醒机制。二者解耦才能稳定协作。
- 初始化 CountDownLatch 时,计数值必须严格等于实际提交的任务数(不是线程池大小,也不是数据源个数——如果某数据源被跳过或拆成多个子任务,要按真实执行单元计数)
- 每个任务执行完毕(无论成功或失败),都必须调用
countDown();漏调会导致主线程永久阻塞 - 主线程在所有任务提交后,立即调用
await()或带超时的await(timeout, unit),不要在任务提交前等待
任务执行中捕获异常并保障 countDown
网络请求类任务易抛异常,若未处理就中断,countDown() 可能不被执行,造成死等。必须确保“执行结束即减一”。
- 把
countDown()放在finally块中,而非仅放在业务逻辑末尾 - 示例结构:
try {<br> result = fetchDataFromSource(source);<br> results.add(result);<br>} catch (Exception e) {<br> log.error("获取数据源 {} 失败", source, e);<br> // 可选:记录失败源,不影响整体计数<br>} finally {<br> latch.countDown(); // 保证执行<br>}
聚合前加入超时控制与状态判断
生产环境不能无限等待。使用带超时的 await(long, TimeUnit),根据业务容忍度设定合理阈值(如 8 秒),避免雪崩。
- 方法返回
false表示超时,此时可主动终止线程池(shutdownNow())、清理资源,并返回降级结果或错误提示 - 即使超时,也要检查是否已有部分结果可利用,比如“3/5 个数据源已返回,先用这三份做近似聚合”
- 避免仅靠
await()无超时版本,尤其在调用外部 HTTP 或数据库场景下风险极高
线程池需按负载定制,而非硬写 Executors 工厂
直接用 Executors.newFixedThreadPool(n) 在生产中易引发 OOM 或拒绝策略失控。应显式构建 ThreadPoolExecutor 并配置核心参数。
- 队列类型建议用
LinkedBlockingQueue(无界)或SynchronousQueue(零容量,适合配合CallerRunsPolicy防溢出) - 拒绝策略推荐
ThreadPoolExecutor.CallerRunsPolicy:当队列满且线程达上限时,由主线程自己执行任务,起到自然限流作用 - 线程数参考公式:
CPU 核数 × (1 + 平均等待时间 / 平均工作时间);IO 密集型任务通常设为核数 2–4 倍

















