phaser 是动态注册、按阶段退出、隐式推进的运行时屏障;必须先 register() 再 arriveandawaitadvance(),否则抛 illegalstateexception;阶段推进靠 arriveandawaitadvance() 原子完成到达与阻塞,phase 从 0 起始;退出用 arriveandderegister();分组协同推荐父子 phaser 树结构。

Phaser 不是预设线程数的静态屏障,而是靠运行时注册、按阶段退出、隐式推进来贴合真实任务流。关键不在“多阶段”本身,而在如何让每个线程在对的阶段加入、停驻或离开。
动态注册必须前置,不能跳过
所有工作线程启动后、执行任务前第一行必须是 phaser.register()。哪怕只启动一个线程,没注册就调 arriveAndAwaitAdvance() 会直接抛 IllegalStateException: Not a registered party。
- 初始化用
new Phaser()或new Phaser(null, 0),从零开始,别写new Phaser(5)—— 那只是设初始计数器,不等于已有合法参与者 - 主线程若参与同步(比如做汇总),也得自己
register();若只协调发任务、等结果,就不注册,改用awaitAdvance(int) - 批量新增可用
bulkRegister(n),比循环调register()略高效,但它仍是动作,不是声明上限
阶段推进靠 arriveAndAwaitAdvance,不是 arrive
arrive() 只上报完成,不阻塞也不推进阶段;真正实现“齐步走”的是 arriveAndAwaitAdvance() —— 它原子完成两件事:标记本线程到达 + 阻塞直到所有当前注册者都到达,然后 phase 自动 +1,线程被唤醒进入下一阶段。
- 阶段编号从 0 开始,首次调用后进入 phase 1,返回值就是新阶段号,可用于分支判断(如
if (phaser.arriveAndAwaitAdvance() == 2)) - 未注册就调用该方法,同样抛异常;phase 是瞬态值,别缓存旧值做逻辑分支,竞态风险高
- 主线程不宜盲目调这个方法,它更适合工作线程;协调角色应优先用
awaitAdvance(int)等待指定阶段结束
阶段性退出必须用 arriveAndDeregister
不是所有线程都要走完全部阶段。数据加载线程做完第 0 阶段就该释放,校验线程做完第 1 阶段就该退出,只留汇总线程全程参与。绕开同步(比如加 if 判断跳过)会让阶段永远卡住。
-
arriveAndDeregister()是原子操作:到达 + 注销一步到位,getRegisteredParties()立即减 1,且能触发阶段自动推进(只要剩余未到达数归零) - 绝不能拆成
arrive()+ “手动注销”——后者根本不存在,deregister()不是 public 方法 - 适合 Map-Reduce 类结构:worker 组计算完注销,collector 线程用
arriveAndAwaitAdvance()等它们全部抵达后开始汇总
分组协同优先用父子 Phaser 树状结构
当任务天然分组(如多个 worker 组各自 map,再统一 reduce),硬塞进单个 Phaser 会导致阶段语义混乱:你无法区分“某组内部完成”和“全局完成”。
- 创建父 Phaser:
Phaser root = new Phaser(); - 每组新建子 Phaser:
Phaser group = new Phaser(root);—— 构造时传入 parent,子 phaser 每完成 own phase,会自动为父 phaser 调用arrive() - 父 phaser 的 phase 推进,代表“所有子组已同步完成当前轮”,天然降低全局竞争压力,适配分片、批量作业等场景
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











