Phaser不是移相器,而是Java并发包中支持动态增减参与者和隐式阶段推进的同步原语;必须显式register()才能参与,用arriveAndDeregister()实现阶段性退出,主线程宜用awaitAdvance(int)精准控阶段,多组协同应采用嵌套Phaser树状结构。

Phaser不是“移相器”,这是常见误译。它本质是Java并发包中专为动态多阶段协作设计的同步原语——核心能力是运行时增减参与者 + 隐式阶段推进,不预设线程数、不依赖固定轮次,适合任务数量不确定、各阶段参与角色不一致的真实业务场景。
必须显式注册,否则永远卡住
Phaser不会自动把线程算作参与者。哪怕只启动一个线程,也得先调phaser.register(),再执行任何arrive*操作。
- 错误写法:
new Phaser(); → 线程直接调用arriveAndAwaitAdvance()→ 抛IllegalStateException: Not a registered party - 正确起点:
new Phaser()或new Phaser(null, 0),然后每个线程干活前第一行就是phaser.register() - 主线程若只做协调(比如发任务、等结果),不必注册;若要参与同步(如汇总线程),也得自己
register()
用arriveAndDeregister()实现“阶段性退出”
不是所有线程都要走完全部阶段。比如:
- 数据加载线程完成第0阶段后,就该释放,不等校验和聚合
- 校验线程做完第1阶段,也不进第2阶段
- 只有汇总线程全程参与
这时不能靠if (phase == X) { skip }绕过同步——未到达的线程会让阶段永远卡住。正确做法是:
- 加载线程:
phaser.arriveAndDeregister()→ 到达+注销,getRegisteredParties()立刻减1 - 校验线程:同样调
arriveAndDeregister()退出 - 汇总线程:用
phaser.arriveAndAwaitAdvance()等待全体抵达,再继续
注意:arriveAndDeregister()是原子操作,别拆成arrive()+“手动注销”——后者根本不存在。
主线程用awaitAdvance(int)精准控节奏
主线程通常不干活,只观察阶段进展。它不该盲目调arriveAndAwaitAdvance(),而应:
- 启动子任务前记录当前阶段:
int p0 = phaser.getPhase() - 分发完所有任务后:
phaser.awaitAdvance(p0)→ 等第0阶段真正结束 - 返回后,再启动第1阶段逻辑(如触发校验)
关键点:
-
awaitAdvance(int)不会自动arrive(),调用者需确保自己已注册并到达(或由其他线程推动) - 返回时
phase已+1,不要缓存旧值做后续判断 - 若等待超时,方法返回当前
phase(等于入参),可据此判断是否卡死
分组协同靠嵌套Phaser,不是硬塞一个
当任务天然分组(如4组worker各自map,再统一reduce),单个Phaser会语义混乱。正确结构:
- 创建父Phaser:
Phaser root = new Phaser() - 每组新建子Phaser:
Phaser group = new Phaser(root)→ 构造时传入parent - 子Phaser每完成一个own phase,自动为父Phaser调一次
arrive() - 父Phaser的phase推进,代表“所有子组均已抵达当前阶段”
这种树状结构天然适配Map-Reduce、流水线分片等模式,避免全局锁死。
不复杂但容易忽略

















