Phaser是Java并发包中支持动态注册/注销参与者和隐式阶段推进的同步工具,适用于任务数不确定、阶段行为不一致的场景;必须显式调用register(),避免IllegalStateException;arrive()留待下一阶段,arriveAndDeregister()退出;awaitAdvance()需先arrive()再等待指定phase;嵌套Phaser可实现分组与全局两级同步;onAdvance()中禁止阻塞操作。

Phaser 不是“移相器”,这是常见误译。它本质是 Java 并发包中支持动态注册/注销参与者 + 隐式阶段推进的同步工具,专为任务数不确定、阶段行为不一致的场景设计。
初始化时别传固定人数,也别用空构造就开跑
直接 new Phaser(5) 会把第一阶段锁死在 5 个参与者,后续无法增减;而 new Phaser() 虽然灵活,但若没人调 register(),线程一调 arriveAndAwaitAdvance() 就立刻返回——因为当前阶段压根没人“守门”,根本不会等待。
- 必须在任务线程真正进入同步逻辑前,显式调用
phaser.register() - 若初始无参与者,就用
new Phaser()或new Phaser(null, 0) - 切忌依赖“自动注册”——
register()是必须手动触发的动作
常见错误现象:IllegalStateException: Not a registered party,基本就是某个线程没 register() 就直接 arrive() 了。
每个阶段用 arrive() 还是 arriveAndDeregister()?看它参不参与下一阶段
arrive() 表示“我这阶段干完了”,但还留在池子里等下一阶段;arriveAndDeregister() 表示“我干完就走,不陪你们进下一轮”。
- 预处理线程只负责加载数据 → 完成后应调
arriveAndDeregister() - 汇总线程要等所有子任务结束再启动 → 应全程用
arriveAndAwaitAdvance() - 主线程若只负责协调(比如分发任务后就退出),也要记得
arriveAndDeregister(),否则会卡住整个 phaser 的 phase 推进
性能影响:频繁 arriveAndDeregister() 会引发内部数组重分配,如果任务粒度极小(如纳秒级计算),建议批量合并或换用 CompletableFuture.join() 链式编排。
用 awaitAdvance() 精确等待某阶段结束,而不是盲目阻塞
phaser.awaitAdvance(int phase) 是最可控的等待方式:它只等指定 phase 结束,不关心当前有多少人注册。
- 启动子任务前记录当前 phase:
int p0 = phaser.getPhase(); - 分发完所有任务后调
phaser.awaitAdvance(p0),确保全部子任务都抵达第 0 阶段 - 再启动第 1 阶段逻辑(如校验),避免“部分子任务还没开始,主线程就往下冲”
注意:awaitAdvance() 不会自动 arrive(),调用者自己得先 arrive() 或 arriveAndDeregister(),否则永远等不到。
嵌套 Phaser 处理分组+全局两级同步
当任务天然分组(如 3 组 worker,每组内同步,组间再统一汇总),用父子 Phaser 最自然:
- 创建父
Phaser root = new Phaser(); - 每组新建子
Phaser group = new Phaser(root);(构造时传 parent) - 子组内线程正常
register()和arriveAndAwaitAdvance() - 子
Phaser完成本阶段时,会自动为父phaser.arrive()
这样,root.awaitAdvance(1) 就代表“所有组都完成了第 1 阶段”,不用手动计数或轮询。
容易踩的坑:onAdvance() 回调里不能做任何可能阻塞的操作(比如再等另一个 Phaser),否则会死锁——这个方法运行在 Phaser 内部锁下。
复杂点在于:phase 推进是隐式的,状态变化不直观。调试时多用 phaser.getRegisteredParties() 和 phaser.getUnarrivedParties() 实时查数,比加日志靠谱得多。

















