
Phaser 是什么,和 CountDownLatch、CyclicBarrier 有什么区别
Phaser 不是“移相器”,是 Java 并发包里一个可动态注册/注销参与者的分阶段同步工具。它比 CountDownLatch 更灵活(支持重用、动态增减线程),也比 CyclicBarrier 更轻量(没有固定 participant 数限制,还能嵌套阶段)。
典型误用是把它当一次性栅栏用——比如只调一次 arriveAndAwaitAdvance() 就结束,结果发现后续调用卡死或抛 IllegalStateException。根本原因是没理解它的“阶段(phase)”模型:每次所有已注册参与者都到达后,phase 自动 +1,下一轮同步才基于新 phase 进行。
怎么正确初始化并让线程加入 Phaser
初始化时别直接 new 一个空 Phaser() 就扔给线程用。如果没预设参与者数,又没手动 register(),线程调 arriveAndAwaitAdvance() 会立刻返回,不等待别人——因为当前 phase 没人“守门”。
- 静态已知线程数:用
new Phaser(int parties),比如启动 4 个 worker,就传 4 - 动态添加:主线程先
new Phaser(),每个 worker 在 run 前调一次phaser.register() - 千万别在构造时传 0,也别靠“运气”等自动注册——
register()是显式动作,必须发生在线程进入同步逻辑前
示例:
Phaser phaser = new Phaser(3); // 主线程 + 2 个 worker<br>new Thread(() -> { /* work */ phaser.arriveAndAwaitAdvance(); }).start();<br>new Thread(() -> { /* work */ phaser.arriveAndAwaitAdvance(); }).start();<br>phaser.arriveAndAwaitAdvance(); // 主线程也参与,凑齐 3 人才推进 phase
立即学习“Java免费学习笔记(深入)”;
arriveAndAwaitAdvance() 卡住?检查 phase 和终止状态
常见现象:线程调了 arriveAndAwaitAdvance() 后永远阻塞。大概率是以下三种之一:
- 当前 phase 已被
forceTermination()强制终止——之后所有 arrive 类操作都立即抛IllegalStateException,但某些 JDK 版本错误信息不提示“terminated”,只说“not registered” - 有线程调过
arriveAndDeregister()离开,但你没更新预期 participant 数,导致剩下的人永远等不到“满员” - 某个线程异常退出没调 arrive,而你用的是固定 parties 初始化,那 phase 就再也不会推进
调试建议:打印 phaser.getPhase() 和 phaser.getRegisteredParties(),对比 phaser.getArrivedParties()。三者不一致,基本就是 deregister 或异常丢失导致的漏登。
多阶段任务怎么用 Phaser 分步控制
Phaser 的核心价值在 stage-aware 同步,比如“所有线程完成读取 → 所有线程完成解析 → 所有线程完成写入”。每轮 arriveAndAwaitAdvance() 都推进 phase,你可以用 phase 值做分支:
int phase = phaser.getPhase();<br>if (phase == 0) { /* 读取阶段 */ }<br>else if (phase == 1) { /* 解析阶段 */ }<br>phaser.arriveAndAwaitAdvance();
注意:getPhase() 返回的是“当前所处 phase”,不是“已完成 phase 数”;刚进入时是 0,第一次 arriveAndAwaitAdvance() 返回后变成 1。别用 phaser.getPhase() == 1 判断“是否完成第一阶段”,而要用返回值:int next = phaser.arriveAndAwaitAdvance(); // next 是下一 phase 编号
容易忽略的一点:Phaser 默认不自动终止,长期运行的服务中若反复注册/注销,得主动调 phaser.bulkRegister(n) 或监控 isTerminated(),否则可能内存泄漏(内部 party list 不收缩)。









