← 返回题目列表

Phaser 是什么?它和 CyclicBarrier、CountDownLatch 有什么区别?

困难 第 31 / 31 题 更新于 2026/07/27
Phaser同步工具阶段栅栏CyclicBarrier

简化版

Phaser(阶段器,Java 7 引入)是一个更灵活的同步工具,可以看成「CyclicBarrier + CountDownLatch 的增强版」——它支持「分阶段(phase)的多线程协同」,且参与线程数可以动态增减(CyclicBarrier/CountDownLatch 的数量是固定的)。核心用法:多个线程在每个「阶段」结束时用 arriveAndAwaitAdvance() 互相等待,都到齐了一起进入下一阶段,可以进行任意多个阶段(CountDownLatch 一次性、CyclicBarrier 每轮固定人数)。它的独特能力:① 动态注册/注销参与者register()/arriveAndDeregister());② 分阶段推进(每个阶段有编号,可用于「分批次、分轮次」的复杂协同);③ 可监听阶段完成(重写 onAdvance)。适合「参与者数量会变、需要多阶段协同」的复杂并发场景。

详细版

Phaser vs CountDownLatch vs CyclicBarrier

维度CountDownLatchCyclicBarrierPhaser
参与者数量固定(构造时定)固定(构造时定)可动态增减
阶段数一次性可循环(每轮固定人数)任意多阶段
灵活性
典型用法等一组任务完成一组线程反复集合参与者变化的多阶段协同

基本用法

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」。