phaser 必须显式注册才能工作,否则 arriveandawaitadvance() 会永久阻塞;其初始注册数为0,不自动计入调用线程,需手动 register(),且 awaitadvance() 不自动触发 arrive()。

Phaser 不是“增强版 CyclicBarrier”,它必须显式注册才能工作,否则 arriveAndAwaitAdvance() 会永久阻塞——这是最常被忽略的前提。
为什么 new Phaser() 后直接调用 arriveAndAwaitAdvance() 就卡死
因为 Phaser 默认初始注册数为 0,不自动把调用线程算作参与者。它不像 CyclicBarrier 那样靠构造参数“锁定人数”,而是完全依赖显式注册动作。
- 主线程没
register()就调arriveAndAwaitAdvance()→ 等一个永远不到的“自己” - 子线程启动后第一行不是
phaser.register()→ 它的到达不会被计数,其他线程永远等不满 - 错误写法:
new Thread(() -> { doWork(); phaser.arriveAndAwaitAdvance(); }).start();—— 缺注册,等于没来过
register() 和 arriveAndDeregister() 怎么配对才不乱阶段
注册和注销不是“开关”,而是对当前阶段参与资格的声明:谁该被计入、谁该被剔除,直接影响 getPhase() 是否推进。
- 初始已知参与者:用
new Phaser(5),表示阶段 0 必须等满 5 个到达 - 运行时新增线程:必须在
doWork()前调phaser.register(),且只能调一次 - 退出后续阶段:用
arriveAndDeregister(),不是“跳过 await”,而是明确告诉 Phaser:“我这阶段到,但下阶段不陪了” - 常见误用:只调
arrive()不调awaitAdvance()→ 阶段不推进;只调awaitAdvance()不先arrive()→ 永远等不到
如何精确等待某阶段结束,而不是盲目同步
awaitAdvance(int phase) 是唯一可控的等待方式,它不关心当前有多少人注册,只盯住目标 phase 是否完成。
- 启动子任务前记下当前阶段:
int p0 = phaser.getPhase(); - 分发完所有任务后,主线程调
phaser.awaitAdvance(p0),确保全部子任务都抵达第 0 阶段 - 再执行第 1 阶段逻辑(如校验),避免“部分还没开始,主线程就往下冲”
- 注意:
awaitAdvance()不会自动arrive(),调用者自己得先触发到达动作
onAdvance() 里能做什么、不能做什么
这个钩子在每个阶段完成、准备推进前被同步调用,是控制流转逻辑的核心出口,但极易踩坑。
- 可以做轻量判断:比如
if (phase == 2 && !checkConsistency()) return true;表示数据不一致就终止 - 可以触发阶段专属动作:如写日志、发通知、清理资源,但必须快——它在内部锁里执行
- 禁止阻塞操作:IO、sleep、数据库写入、远程调用都会拖慢整个阶段切换,甚至引发超时级联
- 返回
true表示终止,之后所有arriveAndAwaitAdvance()返回负值;返回false继续下一阶段
真正难的不是写对 API 调用顺序,而是想清楚每个线程在哪个 phase 注册、在哪个 phase 注销、谁该等谁不该等——Phaser 不管线程生命周期,它只响应你写的 arrive* 调用。错一步,阶段就卡死,而且没有明显错误日志,只有无声的阻塞。










