countdownlatch 适用于等待多个依赖模块检查完成的场景,主线程调用 await() 等待,各模块线程执行后调用 countdown(),需设超时并聚合结果;spring 中可结合 applicationrunner 实现启动前健康检查。

CountDownLatch 是 Java 并发包中用于线程间同步的简单而有效的工具,特别适合“等待一组操作全部完成”的场景。服务启动时,多个依赖模块(如数据库连接、配置加载、缓存初始化、第三方接口连通性检查等)可以并行执行前置检查,主线程只需等待所有检查结束再继续启动流程——这正是 CountDownLatch 的典型用法。
初始化 CountDownLatch 并分发检查任务
在服务启动入口(如 Spring Boot 的 ApplicationRunner 或自定义启动器中),先根据依赖模块数量创建 CountDownLatch 实例,再为每个模块启动一个独立线程执行检查逻辑,检查完成后调用 countDown() 减少计数。
- CountDownLatch 构造参数是总任务数,例如有 4 个依赖模块,就 new CountDownLatch(4)
- 每个检查任务建议封装成 Runnable 或使用 CompletableFuture.runAsync(),避免阻塞主线程
- 务必确保每个任务无论成功或失败都调用 countDown(),否则主线程可能永久阻塞
主线程阻塞等待所有检查完成
主线程调用 await() 方法进入等待状态,直到计数归零或超时。生产环境强烈建议设置超时时间,防止某个模块卡死导致整个服务无法启动。
- 推荐写法:
if (!latch.await(30, TimeUnit.SECONDS)) { throw new IllegalStateException("依赖检查超时"); } - await() 返回 false 表示超时,true 表示正常完成;不要忽略返回值
- 超时后可选择记录未完成的模块、主动中断检查线程(需配合 Thread.interrupt() 和可中断逻辑)
检查结果的收集与错误聚合
CountDownLatch 本身不传递结果,需要额外机制汇总各模块检查状态。常用方式是用线程安全的容器(如 ConcurrentHashMap 或 AtomicReference)记录每个模块的结果。
- 每个检查线程执行完后,把结果(如 Boolean、Exception 或自定义 CheckResult 对象)存入共享 map,key 可为模块名
- await 返回 true 后,遍历结果 map:若任一检查失败,抛出带详情的启动异常
- 避免在检查线程中直接 throw 异常——它不会传播到主线程,只会导致该线程终止且 countDown 被跳过
与 Spring 生命周期结合的实用示例
在 Spring 环境中,可实现 ApplicationRunner 或 SmartInitializingSingleton,在 context 刷新后、Bean 完全可用前执行依赖检查。
- 将 CountDownLatch 和结果容器声明为类成员变量,保证多线程可见性
- 检查任务可通过 TaskExecutor 提交,便于统一管理线程资源
- 若某模块检查失败,可记录日志、发送告警,并让服务快速失败(fail-fast),而不是静默降级
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











