phaser需显式注册线程,否则arriveandawaitadvance()永久阻塞;初始注册数为0,须构造时指定或调用register();用arriveandderegister()退出阶段;onadvance()控制流转;高并发宜用树形phaser。

Phaser 能处理多阶段且参与者动态变化的协作任务,但前提是每个线程必须显式注册,否则 arriveAndAwaitAdvance() 会永久阻塞——这是最常被忽略的前提条件。
为什么 new Phaser() 后直接调用 arriveAndAwaitAdvance() 会卡死
因为 Phaser 不像 CyclicBarrier 那样在构造时就“锁定”线程数;它默认初始注册数为 0,除非你传入非零参数(如 new Phaser(1))或手动调用 register()。主线程若没注册,又调用 arriveAndAwaitAdvance(),就会等一个永远不到的“自己”。
- 构造时指定初始数:适用于已知启动即参与的线程,例如
new Phaser(3)表示预期 3 个线程从阶段 0 就加入 - 运行时注册:新线程启动后第一件事必须是
phaser.register(),且必须在任何arrive*调用之前 - 错误示范:
new Thread(() -> { doWork(); phaser.arriveAndAwaitAdvance(); }).start();—— 缺少register(),该线程的到达不会被计数
如何让线程只参与部分阶段而不影响整体推进
用 arriveAndDeregister() 主动退出,而不是靠“不调用”来跳过后续阶段。退出后,该线程不再计入下阶段的到达计数,但其他注册者仍可继续推进。
-
arriveAndDeregister()返回当前 phase,调用后该线程彻底脱离 Phaser 生命周期 - 注销 ≠ 终止:只要还有注册者,
getPhase()仍会递增,arriveAndAwaitAdvance()仍有效 - 如果想让所有等待立即失败(比如任务被取消),主线程应调用
phaser.forceTermination(),之后所有阻塞中的arriveAndAwaitAdvance()返回负值
onAdvance() 是控制阶段流转逻辑的关键钩子
重写 onAdvance(int phase, int registeredParties) 可以决定是否终止、是否跳过某阶段、或触发阶段专属动作(如写库、发通知)。返回 true 表示终止,后续所有 arriveAndAwaitAdvance() 将立即返回负值。
- 常见用途:在 phase == 2 时执行数据库落盘,然后返回
false继续下一阶段 - 终止条件:比如
if (registeredParties == 0) return true;,表示无人注册时自动结束 - 注意:该方法在内部同步块中执行,不可做耗时操作,否则拖慢整个阶段切换
分层 Phaser(tree phaser)不是噱头,而是解决高并发注册/注销抖动的实用方案
当参与者数量大(比如上百)、且频繁动态增减时,单个 Phaser 的 CAS 竞争会导致 register() 和 arriveAndAwaitAdvance() 延迟升高。这时应按职责或数据分区构建树形结构,上层 Phaser 协调子组,下层各自管理本组线程。
- 例如:10 个数据分片,每片配一个子
Phaser,所有子 phaser 注册到一个根Phaser;各片内完成阶段后,子 phaser 到达,根 phaser 才推进 - 避免滥用:小规模(
- 树形结构需自行维护生命周期,子 phaser 不能独立
forceTermination(),否则可能破坏根节点状态一致性
真正难的不是写对 arriveAndAwaitAdvance(),而是理清每个线程在哪个阶段注册、在哪次调用后注销、以及 onAdvance() 里要不要做副作用操作——这些决策一旦错位,bug 往往表现为阶段卡住、phase 跳变、或线程数统计与实际不符,且难以复现。










