Phaser 分段相位屏障(高级CyclicBarrier+CountDownLatch合体)

一、核心定位

Phaser = 增强版同步屏障,融合了 CountDownLatch(计数等待)+ CyclicBarrier(可复用分批等待),解决二者短板:

  1. CyclicBarrier:参与者数量创建时固定,不能动态增减;只支持单阶段等待;
  2. CountDownLatch:一次性,计数归零不可复用;
  3. Phaser 优势:
    • 参与者(注册线程)动态注册/注销,运行时增减;
    • 多阶段(phase)分段执行,每阶段全部线程抵达才进入下一阶段;
    • 可复用,支持分批次多轮任务;
    • 支持主动退出、批量等待、阶段回调;
      底层同样基于AQS实现。

二、核心核心术语

  1. Participant 参与者
    参与屏障的线程,register() 注册,arriveAndDeregister() 注销;可随时新增/减少。
  2. Phase 相位/阶段
    Phaser 内置自增阶段编号:0 → 1 → 2 ……
    所有参与者完成当前阶段,才自动切换到下一阶段。
  3. arrive:抵达
    线程完成当前阶段任务,告知Phaser“我干完了”,分两种API:
    • arriveAndAwaitAdvance():抵达后阻塞,等所有人抵达再进下一阶段(最常用)
    • arrive():仅标记抵达,不阻塞,适合主线程汇总
    • arriveAndDeregister():抵达后注销自己,不再参与后续阶段

三、关键API分类

1. 注册参与者(新增线程)

int register(); // 注册1个参与者,返回当前阶段号
int bulkRegister(int parties); // 批量注册N个参与者

2. 阶段抵达核心方法(线程完成当前任务调用)

// 1. 抵达并阻塞,等全部参与者完成,再进入下一阶段【业务最常用】
int arriveAndAwaitAdvance();

// 2. 仅标记抵达,不阻塞,不等待其他线程
int arrive();

// 3. 抵达后注销自己,不再参与后续所有阶段
int arriveAndDeregister();

3. 主动等待阶段(主线程专用)

// 等待指定阶段完成,阻塞主线程
boolean awaitAdvance(int phase);
// 限时等待阶段完成
boolean awaitAdvanceInterruptibly(int phase, long timeout, TimeUnit unit);

4. 阶段回调钩子(可选重写)

// 每一个阶段全部线程抵达后,自动执行,返回true则终止Phaser
protected boolean onAdvance(int phase, int registeredParties)
  • phase:当前完成的阶段编号
  • registeredParties:当前剩余注册总线程数
  • 返回 true:Phaser直接终止,所有await线程立即返回;返回false:正常切换下一阶段

四、经典示例:三阶段批处理(读取→解析→入库)

场景:3个工作线程,分3阶段执行,每阶段全部线程完成再走下一阶段

import java.util.concurrent.Phaser;

public class PhaserStageDemo {
    public static void main(String[] args) throws InterruptedException {
        // 自定义Phaser,重写阶段完成回调
        Phaser phaser = new Phaser() {
            // 所有线程完成当前阶段后触发
            @Override
            protected boolean onAdvance(int phase, int registeredParties) {
                System.out.println("===== 阶段" + phase + "全部完成,当前注册线程数:" + registeredParties + " =====");
                // 一共3个阶段 0/1/2,阶段2完成后终止Phaser
                return phase >= 2 || registeredParties == 0;
            }
        };

        // 注册3个工作线程
        int threadNum = 3;
        phaser.bulkRegister(threadNum);

        for (int i = 0; i < threadNum; i++) {
            new Thread(() -> {
                String name = Thread.currentThread().getName();
                try {
                    // ========== 阶段0:读取文件 ==========
                    System.out.println(name + " 阶段0:读取数据");
                    Thread.sleep(200);
                    // 抵达,阻塞等待所有人读完
                    phaser.arriveAndAwaitAdvance();

                    // ========== 阶段1:解析数据 ==========
                    System.out.println(name + " 阶段1:解析数据");
                    Thread.sleep(300);
                    phaser.arriveAndAwaitAdvance();

                    // ========== 阶段2:入库存储 ==========
                    System.out.println(name + " 阶段2:写入数据库");
                    Thread.sleep(100);
                    // 完成所有阶段,注销自身,不再参与后续
                    phaser.arriveAndDeregister();

                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }, "线程-" + i).start();
        }

        // 主线程等待整个Phaser全部执行完毕
        while (!phaser.isTerminated()) {
            Thread.sleep(100);
        }
        System.out.println("所有阶段任务全部结束,Phaser已终止");
    }
}

执行流程说明

  1. 所有线程并行执行阶段0读取,各自调用arriveAndAwaitAdvance()阻塞;
  2. 3个线程全部抵达后,触发onAdvance(0,3),自动切换到阶段1;
  3. 所有线程同时放行,并行执行阶段1解析,再次阻塞等待全员抵达;
  4. 阶段1全部完成,触发回调,切换阶段2;
  5. 阶段2入库完成后,调用arriveAndDeregister()注销线程;
  6. 3个线程全部注销,registeredParties=0,onAdvance返回true,Phaser终止。

五、动态增减参与者示例(CyclicBarrier做不到)

CyclicBarrier创建时固定线程数,运行中无法新增;Phaser支持中途register新增线程:

Phaser phaser = new Phaser(2); // 初始2个线程
// 原有2线程执行阶段0...
// 运行中途新增1个参与者
phaser.register();

六、Phaser vs CyclicBarrier vs CountDownLatch 核心对比

特性 CountDownLatch CyclicBarrier Phaser
参与者数量 固定,创建指定 固定,创建指定 动态,运行时注册/注销
复用性 一次性,不可重置 可循环复用,单阶段 无限复用,多阶段分段
多阶段支持 不支持 仅单阶段 原生多阶段流水线
线程退出 无法主动注销 不能退出 arriveAndDeregister主动注销
完成回调 屏障回调(仅一次) 每阶段完成均可回调onAdvance
使用场景 主线程等待多任务结束、双Latch压测 多线程互相等待,分批并行计算 多阶段流水线、动态任务、复杂分段同步

七、典型业务落地场景

  1. 多阶段流水线任务
    文件处理:读取 → 清洗 → 计算 → 入库;每一步全部线程完成再进入下一环节。
  2. 动态分片并行计算
    运行中根据数据量新增/减少处理线程,CyclicBarrier无法实现。
  3. 多轮次批量任务
    循环多批次处理数据,每批次分多个执行阶段。
  4. 多线程分步初始化
    微服务多组件分阶段启动,阶段1加载配置、阶段2初始化连接池、阶段3对外提供服务。

八、高频易错点

  1. 忘记调用arriveXXX方法
    线程执行完阶段逻辑不调用抵达方法,Phaser永远无法推进阶段,所有线程阻塞死锁。
  2. 混淆arrive() 和 arriveAndAwaitAdvance()
    arrive()仅标记抵达,不会阻塞;子线程必须用arriveAndAwaitAdvance等待其他线程。
  3. onAdvance返回值控制终止
    如果不控制终止条件,Phaser会无限循环阶段0、1、2、3…,不会自动结束。
  4. 并发注册线程注意线程安全
    register/bulkRegister是线程安全方法,多线程同时新增参与者无并发问题。
  5. 终止后不可复用
    isTerminated()=true 屏障彻底失效,无法再次使用,需要新建Phaser。

九、极简使用总结

  1. 固定N线程、单阶段循环同步 → CyclicBarrier;
  2. 一次性等待所有任务完成 → CountDownLatch;
  3. 多阶段流水线 / 动态增减线程 → 优先Phaser。
posted @ 2026-07-25 14:10  七星6609  阅读(12)  评论(0)    收藏  举报