CountDownLatch适用于一次性等待所有线程完成,不可重用;CyclicBarrier支持多轮固定规模同步,可复用并执行屏障动作;Phaser最灵活,支持动态增删线程、多阶段推进及自定义阶段逻辑。

Java并发工具类中,CountDownLatch、CyclicBarrier和Phaser都用于线程协作,但适用场景和能力差异明显。选错工具容易导致代码僵硬、难以扩展,甚至出现资源泄漏或死锁。关键不在“哪个更好”,而在于“哪个更贴合当前任务结构”——尤其是是否需要动态调整参与者、是否涉及多个阶段、是否需复用。
CountDownLatch:适合一次性汇总等待
它像一个单次使用的倒计时门闩,主线程调用 await() 等待子任务全部完成;每个子线程完成时调用 countDown()。计数器归零后,所有阻塞线程被唤醒,且不可重置。
- 典型场景:服务启动时等待数据库连接池、配置加载、缓存预热等依赖项就绪
- 不适用场景:多轮迭代(如训练10轮模型)、中途增删线程、需要阶段间状态传递
- 注意点:若某线程因异常未调用
countDown(),主线程将永久阻塞——建议搭配超时版await(long, TimeUnit)使用
CyclicBarrier:适合固定规模的多轮同步
它像一个可重复使用的“集合点”,所有参与线程必须调用 await() 才能一起放行。每轮结束后自动进入下一轮,支持在放行前执行统一动作(如日志记录、数据校验)。
- 典型场景:并行计算中的多阶段流水线(如每轮迭代后做全局收敛判断)、压测时让N个线程同时发起请求
- 不适用场景:线程数量在运行中变化(比如部分任务提前退出、新任务动态加入)
- 注意点:任一线程中断或超时,整个屏障会破损(
BrokenBarrierException),其他线程也会收到异常,需统一处理恢复逻辑
Phaser:适合动态、分阶段、可伸缩的协同任务
它是三者中最灵活的,本质是“带阶段编号的可变规模屏障”。线程可随时 register() 加入、arriveAndDeregister() 退出;每个阶段由 arriveAndAwaitAdvance() 推进;还可重写 onAdvance() 在阶段切换时插入自定义逻辑(如清理资源、触发告警)。
立即学习“Java免费学习笔记(深入)”;
- 典型场景:分批处理海量数据(每批为一阶段,批次间可动态调整线程数)、微服务中按模块分阶段初始化、游戏服务器中玩家动态加入/退出战斗阶段
- 优势体现:启动10个线程做任务,第3个完成后注销;第7个中途失败自动退出;第12个新线程在第5阶段加入——这些操作对Phaser都是自然支持的
- 注意点:避免频繁注册/注销造成开销;若重写
onAdvance()返回true,Phaser将终止,后续调用会抛IllegalStateException
怎么选?看三个问题
面对一个新并发任务,快速决策可参考:
- 是否只需等一次?→ 选 CountDownLatch
- 是否固定人数、多轮循环、每轮必须全员到齐?→ 选 CyclicBarrier
- 是否人数会变、阶段分明、需复用且有定制需求?→ 选 Phaser
实际项目中,Phaser虽功能强,但学习成本略高;简单场景硬上Phaser反而增加理解负担。合理组合使用才是常态——比如用CountDownLatch启动阶段,再用Phaser管理后续多阶段计算。


















