Phaser 是什么?它和 CyclicBarrier、CountDownLatch 有什么区别?
简化版
Phaser(阶段器,Java 7 引入)是一个更灵活的同步工具,可以看成「CyclicBarrier + CountDownLatch 的增强版」——它支持「分阶段(phase)的多线程协同」,且参与线程数可以动态增减(CyclicBarrier/CountDownLatch 的数量是固定的)。核心用法:多个线程在每个「阶段」结束时用 arriveAndAwaitAdvance() 互相等待,都到齐了一起进入下一阶段,可以进行任意多个阶段(CountDownLatch 一次性、CyclicBarrier 每轮固定人数)。它的独特能力:① 动态注册/注销参与者(register()/arriveAndDeregister());② 分阶段推进(每个阶段有编号,可用于「分批次、分轮次」的复杂协同);③ 可监听阶段完成(重写 onAdvance)。适合「参与者数量会变、需要多阶段协同」的复杂并发场景。
详细版
Phaser vs CountDownLatch vs CyclicBarrier:
| 维度 | CountDownLatch | CyclicBarrier | Phaser |
|---|---|---|---|
| 参与者数量 | 固定(构造时定) | 固定(构造时定) | 可动态增减 |
| 阶段数 | 一次性 | 可循环(每轮固定人数) | 任意多阶段 |
| 灵活性 | 低 | 中 | 高 |
| 典型用法 | 等一组任务完成 | 一组线程反复集合 | 参与者变化的多阶段协同 |
基本用法:
Phaser phaser = new Phaser(3); // 3 个参与者
// 每个线程:
Runnable task = () -> {
// 阶段 0 的工作
doPhase0();
phaser.arriveAndAwaitAdvance(); // 到达并等待其他人(阶段 0 → 1)
// 阶段 1 的工作
doPhase1();
phaser.arriveAndAwaitAdvance(); // 到达并等待(阶段 1 → 2)
// 阶段 2 的工作
doPhase2();
phaser.arriveAndDeregister(); // 完成,注销自己
};
动态注册/注销:
Phaser phaser = new Phaser(1); // 主线程先注册 1 个
for (int i = 0; i < n; i++) {
phaser.register(); // 动态注册一个新参与者
new Thread(() -> {
// ... 工作
phaser.arriveAndDeregister(); // 完成后注销
}).start();
}
phaser.arriveAndDeregister(); // 主线程注销
⚠️ Phaser 的功能强大但复杂、用得少——绝大多数场景 CountDownLatch(等一组任务完成)或 CyclicBarrier(一组线程反复集合)就够了。只有在「参与线程数量会动态变化」或「需要多个阶段的复杂协同」时才需要 Phaser。所以面试考它更多是「知道它的存在和相比前两者的优势(动态参与者 + 多阶段)」,实际编码里能不用就用更简单的 CountDownLatch/CyclicBarrier。
完整版教学
一、Phaser 要解决什么:更灵活的协同
Phaser 是为了解决 CountDownLatch 和 CyclicBarrier 的「不够灵活」而生的:
CountDownLatch 的局限:
一次性(count 减到 0 就失效,不能重用)
参与者数量固定(构造时定死)
CyclicBarrier 的局限:
虽然可循环,但每轮的参与人数固定(构造时定死)
不支持"多个不同阶段"的灵活协同
Phaser 补上:
① 参与者数量可以动态增减(运行时 register/deregister)
② 支持任意多个阶段(不只是"一轮",而是"多轮/多阶段")
③ 更灵活的到达/等待语义(arrive、arriveAndAwait、arriveAndDeregister)
Phaser 的价值是「在 CountDownLatch/CyclicBarrier 之上,提供动态参与者 + 多阶段的灵活协同」。它适合「参与线程数量会变、且需要分多个阶段协同」的复杂场景——比如「一批任务分阶段执行,中途可能有任务加入或退出」。理解「Phaser 是更灵活的同步工具(动态参与者 + 多阶段),补 CountDownLatch/CyclicBarrier 的固定人数/固定轮次的不足」,就理解了它的定位——它是「重量级、灵活」的协同工具。
二、分阶段推进:phase 的概念
Phaser 的核心概念是「阶段(phase)」——多线程可以分多个阶段协同,每个阶段有一个编号:
Phaser 的阶段推进:
所有参与者在当前阶段做完工作后,调 arriveAndAwaitAdvance()
→ 等所有参与者都"到达"当前阶段的终点
→ 都到齐后,Phaser 的阶段号 +1(推进到下一阶段),所有线程一起继续
阶段 0:所有线程做阶段 0 的工作 → 都 arriveAndAwaitAdvance → 进入阶段 1
阶段 1:所有线程做阶段 1 的工作 → 都 arriveAndAwaitAdvance → 进入阶段 2
...可以进行任意多个阶段
getPhase() 能获取当前阶段号
「分阶段」让 Phaser 能表达「多轮/多步骤的协同」——比 CyclicBarrier(虽然可循环,但只是「反复同一个集合点」)更结构化。每个阶段是一个明确的「协同点」,所有参与者在这里对齐、然后一起进入下一阶段。这适合「计算分多个阶段、每阶段结束要同步」的场景(如迭代计算:每轮迭代结束所有线程对齐、进入下一轮)。理解「Phaser 分阶段(phase)推进、都到齐进入下一阶段、支持任意多阶段」,就掌握了它「多阶段协同」的核心能力。
三、动态注册与注销
Phaser 最独特的能力是「参与者可以动态增减」——这是 CountDownLatch/CyclicBarrier 做不到的:
Phaser phaser = new Phaser(0); // 初始 0 个参与者
// 动态注册(增加参与者)
phaser.register(); // 参与者 +1
phaser.bulkRegister(5); // 一次注册 5 个
// 注销(减少参与者)
phaser.arriveAndDeregister(); // 到达并注销自己(参与者 -1)
关键方法:① register()——运行时新增一个参与者(参与者数 +1);② arriveAndDeregister()——一个参与者完成后注销自己(到达当前阶段 + 参与者数 -1,之后不再参与后续阶段)。这让 Phaser 能应对「参与者数量在运行时变化」的场景——比如「任务动态创建(register)、完成后退出(deregister)」。而 CountDownLatch/CyclicBarrier 的数量在构造时就定死了,无法动态调整。这个「动态参与者」能力是 Phaser 最有价值的特性。理解「Phaser 可动态 register 新增参与者、arriveAndDeregister 注销、应对参与者数量变化」,就掌握了它区别于前两者的核心优势。
四、arrive 系列方法的区别
Phaser 有几个「到达」相关的方法,语义不同,容易混:
arrive():
报告"我到达了当前阶段",但不等待其他人 → 立即返回,继续往下执行
(只是"打个卡",不阻塞)
arriveAndAwaitAdvance():
报告到达 + 等待其他人都到达 → 阻塞,直到所有参与者到齐、进入下一阶段
(最常用,类似 CyclicBarrier.await)
arriveAndDeregister():
报告到达 + 注销自己 → 不再参与后续阶段,立即返回
(完成任务、退出协同)
awaitAdvance(phase):
等待指定阶段完成(不报告到达)
三个核心方法:① arrive()(只报告到达、不等待);② arriveAndAwaitAdvance()(报告到达 + 等所有人到齐、进入下一阶段,最常用);③ arriveAndDeregister()(报告到达 + 注销、退出)。它们对应不同的协同需求——「打卡就走」用 arrive、「等大家一起走」用 arriveAndAwaitAdvance、「干完退出」用 arriveAndDeregister。这套细粒度的方法让 Phaser 能表达复杂的协同逻辑。理解「arrive 只到达不等、arriveAndAwaitAdvance 到达并等(最常用)、arriveAndDeregister 到达并注销」,就掌握了 Phaser 的核心 API。
五、onAdvance:监听阶段完成
Phaser 还能「监听每个阶段的完成」——重写 onAdvance 方法,在每个阶段推进时执行自定义逻辑:
Phaser phaser = new Phaser(3) {
@Override
protected boolean onAdvance(int phase, int registeredParties) {
System.out.println("阶段 " + phase + " 完成,参与者数 " + registeredParties);
// 返回 true 表示"终止 Phaser"(所有阶段结束)
// 返回 false 表示"继续下一阶段"
return phase >= 2 || registeredParties == 0; // 阶段 2 完成后或没参与者时终止
}
};
onAdvance(phase, registeredParties) 在「一个阶段的所有参与者都到齐、即将推进到下一阶段时」被调用——类似 CyclicBarrier 的 barrierAction,但更强:① 能拿到当前阶段号和参与者数;② 返回值控制是否终止 Phaser(返回 true 则 Phaser 终止,所有等待的线程被释放、不再有后续阶段)。这让 Phaser 能实现「在某个条件下自动终止」(如「进行到第 N 阶段」或「没有参与者了」)。这是 CyclicBarrier 的 barrierAction 没有的控制能力。理解「onAdvance 监听阶段完成、能拿阶段号和参与者数、返回 true 终止 Phaser」,就掌握了 Phaser 的阶段监听能力。
六、选型:什么时候用 Phaser
Phaser 功能强但复杂,选型要务实:
能用简单的就别用 Phaser:
只是"等一组任务完成"(一次性)→ CountDownLatch
只是"一组固定线程反复集合"→ CyclicBarrier
→ 这两个更简单、够用,绝大多数场景选它们
只有这些情况才用 Phaser:
✓ 参与线程数量会动态变化(运行时有线程加入/退出)
✓ 需要多个不同阶段的复杂协同(不只是一轮集合)
✓ 需要在阶段完成时做条件判断/自动终止(onAdvance)
现实:
Phaser 用得很少,大多数并发协同 CountDownLatch/CyclicBarrier 就够
面试考它主要是"知道有这个更灵活的工具、理解它的优势(动态参与者+多阶段)"
核心判断:默认用 CountDownLatch(一次性等待)或 CyclicBarrier(固定人数反复集合),只有「参与者数量动态变化」或「多阶段复杂协同」时才用 Phaser。Phaser 是「重武器」——功能全但复杂,能不用就不用(简单工具更易读易维护)。这和「能用 volatile 就别用锁、能用简单同步就别用复杂的」是同一个「奥卡姆剃刀」原则。理解「默认用 CountDownLatch/CyclicBarrier、只在动态参与者/多阶段时用 Phaser、能简单就别复杂」,就掌握了它的选型——知道它存在、知道何时用即可。
记忆钩子:「Phaser 是更灵活的同步工具(CyclicBarrier + CountDownLatch 增强版):支持多阶段(phase)协同 + 参与者动态增减(register/arriveAndDeregister,前两者数量固定);核心方法 arrive(只到达不等)、arriveAndAwaitAdvance(到达并等所有人、进下一阶段,最常用)、arriveAndDeregister(到达并注销退出);onAdvance 监听阶段完成(返回 true 终止 Phaser);功能强但复杂用得少,默认用 CountDownLatch/CyclicBarrier,只在动态参与者/多阶段时用它」。
七、常见误区与追问
- 误区:Phaser 和 CyclicBarrier 一样。 Phaser 更灵活——参与者数量可动态增减(register/deregister)、支持任意多阶段、能监听阶段完成并控制终止;CyclicBarrier 参与人数固定、只是反复同一个集合点。
- 误区:Phaser 应该优先使用。 它功能强但复杂,绝大多数场景 CountDownLatch/CyclicBarrier 就够且更简单;只在「参与者动态变化」或「多阶段复杂协同」时才用 Phaser。
- 误区:arrive() 会等待其他线程。 arrive() 只报告到达、不等待(立即返回);等待用 arriveAndAwaitAdvance()(到达并等所有人到齐、进入下一阶段)。
- 误区:Phaser 的参与者数量也是固定的。 可动态增减——register() 新增参与者、arriveAndDeregister() 注销,这是它区别于 CountDownLatch/CyclicBarrier(数量固定)的核心优势。
- 追问:Phaser 相比 CountDownLatch/CyclicBarrier 的优势? 参与者数量可动态增减(前两者固定)、支持任意多阶段协同(CountDownLatch 一次性、CyclicBarrier 反复同一集合点)、可监听阶段完成并控制终止(onAdvance);适合参与者变化的多阶段复杂场景。
- 追问:arriveAndAwaitAdvance 和 arriveAndDeregister 的区别? arriveAndAwaitAdvance 报告到达 + 等所有参与者到齐、进入下一阶段(继续参与);arriveAndDeregister 报告到达 + 注销自己、退出(不再参与后续阶段)。
- 追问:什么时候该用 Phaser? 参与线程数量运行时会变化(动态加入/退出)、需要多个阶段的复杂协同、需要在阶段完成时条件判断/自动终止时;否则用更简单的 CountDownLatch/CyclicBarrier。
八、加强记忆
Phaser(阶段器,Java 7)是更灵活的同步工具,可看成「CyclicBarrier + CountDownLatch 的增强版」,两大核心优势:① 参与者数量可动态增减(register() 新增、arriveAndDeregister() 注销,而 CountDownLatch/CyclicBarrier 的数量构造时固定);② 支持任意多个阶段(phase)协同(CountDownLatch 一次性、CyclicBarrier 只是反复同一集合点)。核心方法:arrive()(只报告到达不等待)、arriveAndAwaitAdvance()(到达并等所有参与者到齐、进入下一阶段,最常用类似 CyclicBarrier.await)、arriveAndDeregister()(到达并注销退出);getPhase() 取阶段号;重写 onAdvance(phase, parties) 可监听阶段完成(能拿阶段号和参与者数、返回 true 终止 Phaser)。它功能强但复杂、用得少——绝大多数场景 CountDownLatch(等一组任务完成)或 CyclicBarrier(固定人数反复集合)就够且更简单,只在「参与者数量动态变化」或「多阶段复杂协同」时才用 Phaser(能简单就别复杂)。面试主要考「知道它存在、理解它相比前两者的优势」。一句话「Phaser 是灵活同步工具(动态参与者+多阶段),arrive 只到达/arriveAndAwaitAdvance 到达并等/arriveAndDeregister 注销退出、onAdvance 监听阶段,功能强但复杂用得少,默认用 CountDownLatch/CyclicBarrier」。