【Java源码阅读系列26】深度解读Java CyclicBarrier 源码

CyclicBarrier 是 Java 并发包(java.util.concurrent)中一个经典的同步工具类,由 Doug Lea 设计,用于协调多个线程在某个“屏障点”等待彼此,直到所有线程都到达后再继续执行。其核心特性是可重复使用(Cyclic),适用于需要多线程分阶段协作的场景。本文将从源码入手,结合设计模式分析其实现原理,并给出常见使用场景与代码示例。

一、核心设计:基于锁与条件变量的循环屏障

1. 类结构概览

CyclicBarrier 的核心逻辑依赖 ReentrantLockCondition实现线程的阻塞与唤醒,并通过内部类 Generation 管理“代”的状态,确保屏障可以循环使用。

public class CyclicBarrier {
    private static class Generation {
        boolean broken = false; // 屏障是否被破坏
    }

    private final ReentrantLock lock = new ReentrantLock(); // 互斥锁
    private final Condition trip = lock.newCondition(); // 条件变量,用于线程等待
    private final int parties; // 需要等待的线程总数
    private final Runnable barrierCommand; // 屏障触发时执行的任务
    private Generation generation = new Generation(); // 当前“代”
    private int count; // 剩余等待的线程数(初始为 parties)
}

2. 关键方法源码解析

(1) 构造函数:初始化屏障参数

CyclicBarrier 有两个构造函数,支持传入线程数 parties 和可选的屏障任务 barrierCommand(所有线程到达后执行)。

public CyclicBarrier(int parties) {
    this(parties, null);
}

public CyclicBarrier(int parties, Runnable barrierCommand) {
    if (parties <= 0) throw new IllegalArgumentException();
    this.parties = parties;
    this.count = parties; // 初始剩余等待线程数为 parties
    this.barrierCommand = barrierCommand;
}

(2) await():核心等待逻辑

await() 方法是 CyclicBarrier 的核心,其内部调用 dowait() 实现具体逻辑。线程调用 await() 后会阻塞,直到所有 parties 线程都到达屏障,或屏障被破坏(如超时、中断)。

public int await() throws InterruptedException, BrokenBarrierException {
    return dowait(false, 0L);
}

private int dowait(boolean timed, long nanos) 
    throws InterruptedException, BrokenBarrierException, TimeoutException {
    final ReentrantLock lock = this.lock;
    lock.lock(); // 加锁保证原子性
    try {
        final Generation g = generation;
        if (g.broken) throw new BrokenBarrierException(); // 屏障已破坏

        if (Thread.interrupted()) { // 当前线程被中断
            breakBarrier(); // 标记屏障破坏并唤醒所有线程
            throw new InterruptedException();
        }

        int index = --count; // 剩余等待线程数减 1
        if (index == 0) { // 所有线程已到达
            try {
                if (barrierCommand != null) barrierCommand.run(); // 执行屏障任务
                nextGeneration(); // 重置屏障到下一代
                return 0; // 最后到达的线程返回 0
            } finally {
                if (!ranAction) breakBarrier(); // 屏障任务执行失败时标记破坏
            }
        }

        // 未到达的线程进入循环等待
        for (;;) {
            try {
                if (!timed) trip.await(); // 无超时等待
                else if (nanos > 0) nanos = trip.awaitNanos(nanos); // 超时等待
            } catch (InterruptedException ie) {
                if (g == generation && !g.broken) { // 等待期间被中断
                    breakBarrier();
                    throw ie;
                } else {
                    Thread.currentThread().interrupt(); // 其他情况恢复中断状态
                }
            }

            if (g.broken) throw new BrokenBarrierException(); // 屏障被破坏
            if (g != generation) return index; // 成功进入下一代,返回当前线程的到达顺序
            if (timed && nanos <= 0) { // 超时处理
                breakBarrier();
                throw new TimeoutException();
            }
        }
    } finally {
        lock.unlock(); // 释放锁
    }
}

关键逻辑说明

  • 加锁保证原子性:通过 ReentrantLock 保证对 countgeneration 的修改是原子的。
  • Generation 代机制:每次屏障触发(所有线程到达)或重置(reset())时,生成新的 Generation 实例,避免旧代的线程干扰新代。
  • 条件变量等待:未到达的线程通过 trip.await() 阻塞,直到最后一个线程到达后调用 trip.signalAll() 唤醒所有线程。
  • 异常处理:若线程被中断或超时,调用 breakBarrier() 标记屏障破坏(generation.broken = true),并唤醒所有等待线程,避免线程永久阻塞。

(3) reset():重置屏障

reset() 方法用于将屏障重置为初始状态,中断当前所有等待的线程并生成新的代。

public void reset() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        breakBarrier(); // 破坏当前代,唤醒所有线程
        nextGeneration(); // 生成新代,重置 count
    } finally {
        lock.unlock();
    }
}

private void breakBarrier() {
    generation.broken = true;
    count = parties; // 重置剩余等待数
    trip.signalAll(); // 唤醒所有等待线程
}

private void nextGeneration() {
    trip.signalAll(); // 唤醒旧代线程(实际已处理)
    count = parties; // 重置剩余等待数
    generation = new Generation(); // 新代
}

二、设计模式分析

1. 状态模式(State Pattern)

CyclicBarrier 通过 Generation 类表示屏障的状态(如 broken 标记)。不同状态下,await() 方法的行为不同:

  • generation.brokentrue(屏障被破坏),所有后续 await() 调用会直接抛出 BrokenBarrierException
  • generation 切换(进入新代),等待线程被唤醒并继续执行。

状态模式将状态相关的行为封装在 Generation 中,简化了屏障的循环复用逻辑。

2. 模板方法模式(Template Method Pattern)

CyclicBarrier 的核心逻辑(如加锁、条件等待、状态检查)封装在 dowait() 方法中,而具体的屏障任务(barrierCommand)由用户传入。这种设计允许用户自定义屏障触发时的行为,符合“开闭原则”。

3. 代理模式(Proxy Pattern)

CyclicBarrier 的外部接口(如 awaitreset)实际委托给内部的 ReentrantLockCondition 实现。外部类隐藏了锁与条件变量的复杂操作,提供了更简洁的 API。


三、常见使用场景与代码示例

1. 场景 1:多线程分阶段任务协作(如并行计算后合并结果)

多个线程并行处理子任务,完成后等待所有线程到达屏障,最后由一个线程合并结果。

import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ParallelComputationExample {
    private static final int N = 3; // 线程数
    private static final int[] data = {1, 2, 3, 4, 5, 6}; // 原始数据
    private static int[] results = new int[N]; // 子任务结果

    public static void main(String[] args) {
        // 定义屏障任务:合并子任务结果
        Runnable mergeTask = () -> {
            int sum = 0;
            for (int res : results) sum += res;
            System.out.println("合并结果:" + sum);
        };

        CyclicBarrier barrier = new CyclicBarrier(N, mergeTask);
        ExecutorService executor = Executors.newFixedThreadPool(N);

        for (int i = 0; i < N; i++) {
            final int threadId = i;
            executor.submit(() -> {
                // 子任务:计算数据块的和
                int start = threadId * (data.length / N);
                int end = (threadId == N - 1) ? data.length : (threadId + 1) * (data.length / N);
                int sum = 0;
                for (int j = start; j < end; j++) sum += data[j];
                results[threadId] = sum;
                System.out.println("线程 " + threadId + " 完成计算,结果:" + sum);

                try {
                    barrier.await(); // 等待其他线程到达屏障
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }

        executor.shutdown();
    }
}

输出示例:

线程 0 完成计算,结果:3
线程 1 完成计算,结果:7
线程 2 完成计算,结果:11
合并结果:21

2. 场景 2:多线程等待所有线程准备就绪(如游戏加载)

多个玩家线程加载资源,全部加载完成后启动游戏。

import java.util.concurrent.CyclicBarrier;

public class GameStartExample {
    public static void main(String[] args) {
        int playerCount = 3;
        CyclicBarrier barrier = new CyclicBarrier(playerCount, 
            () -> System.out.println("所有玩家加载完成,游戏开始!"));

        for (int i = 0; i < playerCount; i++) {
            final int playerId = i;
            new Thread(() -> {
                try {
                    System.out.println("玩家 " + playerId + " 开始加载...");
                    Thread.sleep((long) (Math.random() * 2000)); // 模拟加载时间
                    System.out.println("玩家 " + playerId + " 加载完成");
                    barrier.await(); // 等待其他玩家
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

输出示例:

玩家 0 开始加载...
玩家 1 开始加载...
玩家 2 开始加载...
玩家 1 加载完成
玩家 0 加载完成
玩家 2 加载完成
所有玩家加载完成,游戏开始!

3. 场景 3:循环执行的多阶段任务(如定期数据处理)

多个线程分阶段处理数据,每个阶段结束后等待所有线程,循环执行。

import java.util.concurrent.CyclicBarrier;

public class CyclicTaskExample {
    public static void main(String[] args) {
        int phaseCount = 2; // 阶段数
        int threadCount = 2; // 线程数
        CyclicBarrier barrier = new CyclicBarrier(threadCount);

        for (int i = 0; i < threadCount; i++) {
            final int threadId = i;
            new Thread(() -> {
                for (int phase = 0; phase < phaseCount; phase++) {
                    System.out.println("线程 " + threadId + " 执行阶段 " + phase);
                    try {
                        barrier.await(); // 阶段结束,等待其他线程
                        System.out.println("线程 " + threadId + " 进入阶段 " + (phase + 1));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
}

输出示例:

线程 0 执行阶段 0
线程 1 执行阶段 0
线程 0 进入阶段 1
线程 1 进入阶段 1
线程 0 执行阶段 1
线程 1 执行阶段 1
线程 0 进入阶段 2
线程 1 进入阶段 2

四、总结

CyclicBarrier 是基于 ReentrantLockCondition 实现的循环屏障工具,通过 Generation 机制支持重复使用,适用于多线程分阶段协作场景。其核心设计模式包括状态模式(管理屏障状态)、模板方法模式(自定义屏障任务)和代理模式(隐藏底层同步细节)。理解其源码有助于更高效地使用该工具,并掌握 Java 并发包的设计思想。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值