Phaser是Java并发包中专为动态多阶段协作设计的同步原语,非硬件移相器;register()必须在arrive*前调用,否则线程阻塞;需动态注册/注销,慎用预设初始数;多阶段推进依赖getPhase()与awaitAdvance()配合;嵌套Phaser可构建树形协同结构。

Phaser 不是“移相器”,它是 Java 并发包中专为动态多阶段协作设计的同步原语;用错命名或类比(比如和硬件移相器类比)会直接导致理解偏差,进而引发 IllegalStateException 或死锁。
register() 必须在 arrive* 之前调用,否则线程永远卡住
常见错误是构造 Phaser 后直接让子线程调用 arriveAndAwaitAdvance(),结果所有线程阻塞在第一阶段——因为没人注册。
- 主线程若参与协调,需显式调用
phaser.register()(哪怕只用它做 await 控制) - 子线程必须在执行任何
arrive()、arriveAndAwaitAdvance()或arriveAndDeregister()前完成register() - 调试时可随时检查:
phaser.getRegisteredParties()应大于 0,phaser.getUnarrivedParties()应等于已注册数(刚注册完未到达时)
动态增减参与者要用 arriveAndDeregister(),别靠 new Phaser(n) 预设
预设初始数(如 new Phaser(5))只适合参与者数量完全确定的场景;一旦任务来自队列拉取、递归分治或响应式流,就必须用运行时注册机制。
- 新增任务:在任务启动前调用
phaser.register(),它会立即增加getRegisteredParties()计数 - 任务退出:用
phaser.arriveAndDeregister(),它原子完成“到达 + 注销”,触发阶段推进(如果当前阶段已满) - 避免高频小任务频繁注册/注销:若单任务耗时远小于纳秒级,考虑批量合并或换用
CompletableFuture链式编排
多阶段推进靠 getPhase() + awaitAdvance(int),不是靠循环调用 arriveAndAwaitAdvance()
arriveAndAwaitAdvance() 是“到达并等待本阶段结束”,它隐式推进 phase;但若某阶段只需部分线程参与(如仅汇总线程执行第 3 阶段),就不能让所有线程都调这个方法。
- 主线程控制节奏:用
phaser.awaitAdvance(phaser.getPhase())等待当前阶段全体完成,再决定是否新增参与者或启动下一阶段逻辑 - 条件性执行:在任务内用
if (phaser.getPhase() == 2) { doSummary(); },然后统一调arriveAndDeregister()退出 - 注意:
awaitAdvance(int)返回后,phase 可能已跳变(因其他线程提前推进),所以不要缓存 phase 值做后续判断
嵌套 Phaser 构建树形协同结构,避免单点瓶颈
当任务天然分组(如 4 组 worker 各自处理一批数据,再由 coordinator 汇总),平铺式用一个 Phaser 会导致高竞争和扩展性差;应使用父子嵌套。
- 创建父
Phaser root = new Phaser(),每组新建子Phaser group = new Phaser(root) - 子
Phaser完成本组阶段时,自动为父Phaser调用arrive(),无需手动通知 - 父
Phaser的getPhase()表示“所有子组均已抵达该阶段”,天然适配 Map-Reduce 类模式 - 子
Phaser可独立注销(forceTermination()),不影响父节点继续协调其他组
真正难的不是写对 API 调用顺序,而是判断哪些线程该在哪一阶段注册、哪一阶段注销、哪一阶段只旁观不参与——这需要把业务流程先拆成明确的同步点,再映射到 register()/arriveAndDeregister() 的时机上。漏掉一个注册,整个阶段就悬停;多注销一个,阶段可能提前推进导致数据未就绪。










