CyclicBarrier 循环栅栏

本文深入解析CyclicBarrier的使用方法,包括其构造方法、主要功能和应用场景,通过实例代码演示了如何利用CyclicBarrier实现多线程的同步控制,特别强调了其循环使用的特点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CountDownLatch 倒计数只有一次,倒计数到0,则结束。有时候的需求是一批一批的倒计数

比如主线程没十个子线程完成一次,则主线程继续执行一次,下次又来了十个线程完成,则主线程

在执行一次。循环的倒计数,则需要用到循环栅栏CyclicBarrier 。

构造方法为public CyclicBarrier(int parties, Runnable barrierAction)

其中parties,指的就是倒计数的个数。barrierAction指的是每当倒计数到0时需要执行

的一个系统操作。

CyclicBarrier的简单理解

其实,我更喜欢[人满发车]这个词来理解CyclicBarrier的作用:

长途汽车站提供长途客运服务。
当等待坐车的乘客到达20人时,汽车站就会发出一辆长途汽车,让这20个乘客上车走人。
等到下次等待的乘客又到达20人是,汽车站就会又发出一辆长途汽车。

CyclicBarrier的应用场景

CyclicBarrier常用于多线程分组计算。

2.CyclicBarrier方法说明
CyclicBarrier提供的方法有:

——CyclicBarrier(parties)

初始化相互等待的线程数量的构造方法。

——CyclicBarrier(parties,Runnable barrierAction)

初始化相互等待的线程数量以及屏障线程的构造方法。

屏障线程的运行时机:等待的线程数量=parties之后,CyclicBarrier打开屏障之前。

举例:在分组计算中,每个线程负责一部分计算,最终这些线程计算结束之后,交由屏障线程进行汇总计算。

——getParties()

获取CyclicBarrier打开屏障的线程数量,也成为方数。

——getNumberWaiting()

获取正在CyclicBarrier上等待的线程数量。

——await()

在CyclicBarrier上进行阻塞等待,直到发生以下情形之一:

在CyclicBarrier上等待的线程数量达到parties,则所有线程被释放,继续执行。
当前线程被中断,则抛出InterruptedException异常,并停止等待,继续执行。
其他等待的线程被中断,则当前线程抛出BrokenBarrierException异常,并停止等待,继续执行。
其他等待的线程超时,则当前线程抛出BrokenBarrierException异常,并停止等待,继续执行。
其他线程调用CyclicBarrier.reset()方法,则当前线程抛出BrokenBarrierException异常,并停止等待,继续执行。
——await(timeout,TimeUnit)

在CyclicBarrier上进行限时的阻塞等待,直到发生以下情形之一:

在CyclicBarrier上等待的线程数量达到parties,则所有线程被释放,继续执行。
当前线程被中断,则抛出InterruptedException异常,并停止等待,继续执行。
当前线程等待超时,则抛出TimeoutException异常,并停止等待,继续执行。
其他等待的线程被中断,则当前线程抛出BrokenBarrierException异常,并停止等待,继续执行。
其他等待的线程超时,则当前线程抛出BrokenBarrierException异常,并停止等待,继续执行。
其他线程调用CyclicBarrier.reset()方法,则当前线程抛出BrokenBarrierException异常,并停止等待,继续执行。
——isBroken()

获取是否破损标志位broken的值,此值有以下几种情况:

CyclicBarrier初始化时,broken=false,表示屏障未破损。
如果正在等待的线程被中断,则broken=true,表示屏障破损。
如果正在等待的线程超时,则broken=true,表示屏障破损。
如果有线程调用CyclicBarrier.reset()方法,则broken=false,表示屏障回到未破损状态。
——reset()

使得CyclicBarrier回归初始状态,直观来看它做了两件事:

如果有正在等待的线程,则会抛出BrokenBarrierException异常,且这些线程停止等待,继续执行。
将是否破损标志位broken置为false。
3.CyclicBarrier方法练习
3.1.练习一
练习目的:

了解CyclicBarrier(parties)/getParties()/await()/getNumberWaiting()的基本用法。
理解循环的意义。
示例代码:
 

 //构造函数1:初始化-开启屏障的方数
CyclicBarrier barrier0 = new CyclicBarrier(2);
//通过barrier.getParties()获取开启屏障的方数
LOGGER.info("barrier.getParties()获取开启屏障的方数:" + barrier0.getParties());
System.out.println();
//通过barrier.getNumberWaiting()获取正在等待的线程数
LOGGER.info("通过barrier.getNumberWaiting()获取正在等待的线程数:初始----" + barrier0.getNumberWaiting());
System.out.println();
new Thread(() -> {
    //添加一个等待线程
    LOGGER.info("添加第1个等待线程----" + Thread.currentThread().getName());
    try {
        barrier0.await();
        LOGGER.info(Thread.currentThread().getName() + " is running...");
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (BrokenBarrierException e) {
        e.printStackTrace();
    }
    LOGGER.info(Thread.currentThread().getName() + " is terminated.");
}).start();
Thread.sleep(10);
//通过barrier.getNumberWaiting()获取正在等待的线程数
LOGGER.info("通过barrier.getNumberWaiting()获取正在等待的线程数:添加第1个等待线程---" + barrier0.getNumberWaiting());
Thread.sleep(10);
System.out.println();
new Thread(() -> {
    //添加一个等待线程
    LOGGER.info("添加第2个等待线程----" + Thread.currentThread().getName());
    try {
        barrier0.await();
        LOGGER.info(Thread.currentThread().getName() + " is running...");
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (BrokenBarrierException e) {
        e.printStackTrace();
    }
    LOGGER.info(Thread.currentThread().getName() + " is terminated.");
}).start();
Thread.sleep(100);
System.out.println();
//通过barrier.getNumberWaiting()获取正在等待的线程数
LOGGER.info("通过barrier.getNumberWaiting()获取正在等待的线程数:打开屏障之后---" + barrier0.getNumberWaiting());

//已经打开的屏障,再次有线程等待的话,还会重新生效--视为循环
new Thread(() -> {
    LOGGER.info("屏障打开之后,再有线程加入等待:" + Thread.currentThread().getName());
    try {
        //BrokenBarrierException
        barrier0.await();
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (BrokenBarrierException e) {
        e.printStackTrace();
    }
    LOGGER.info(Thread.currentThread().getName() + " is terminated.");

}).start();
System.out.println();
Thread.sleep(10);
LOGGER.info("通过barrier.getNumberWaiting()获取正在等待的线程数:打开屏障之后---" + barrier0.getNumberWaiting());
Thread.sleep(10);
new Thread(() -> {
    LOGGER.info("屏障打开之后,再有线程加入等待:" + Thread.currentThread().getName());
    try {
        //BrokenBarrierException
        barrier0.await();
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (BrokenBarrierException e) {
        e.printStackTrace();
    }
    LOGGER.info(Thread.currentThread().getName() + " is terminated.");

}).start();
Thread.sleep(10);
LOGGER.info("通过barrier.getNumberWaiting()获取正在等待的线程数:打开屏障之后---" + barrier0.getNumberWaiting());

    下面是一个士兵集合及士兵完成任务的demo

import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * @Author:minglu
 * @Description:
 * @Date: 2018/11/27
 */
public class CyclicBarrierDemo {
    public static class Soldier implements Runnable{
        private String soldier;
        private final CyclicBarrier cyclicBarrier;

        public Soldier(String soldier, CyclicBarrier cyclicBarrier) {
            this.soldier = soldier;
            this.cyclicBarrier = cyclicBarrier;
        }

        @Override
        public void run() {
            try {
                //等待士兵集合完毕
                cyclicBarrier.await();
                doWork();
                //等待所有士兵完成工作
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
        public void doWork(){
            try {
                Thread.sleep(new Random().nextInt(10)*1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(soldier+"任务完成!");
        }
    }

    public static class BarrierRun implements Runnable{
        boolean flag;
        int n;

        public BarrierRun(boolean flag, int n) {
            this.flag = flag;
            this.n = n;
        }

        @Override
        public void run() {
            if (flag){
                System.out.println("司令:【士兵 " + n +"个,任务完成!");
            }else{
                System.out.println("司令:【士兵 " + n +"个,集合完毕!");
                flag = true;
            }
        }
    }

    public static void main(String[] args) {
        final int n = 10;
        Thread[] soldiers = new Thread[n];
        boolean flag = false;
        CyclicBarrier cyclicBarrier = new CyclicBarrier(n,new BarrierRun(flag,n));
        //设置屏障点,主要是为了执行这个方法
        System.out.println("集合队伍!");
        for (int i = 0; i <n ; ++i) {
            System.out.println("士兵" + i + "报道!");
            soldiers[i] = new Thread(new Soldier("士兵"+i,cyclicBarrier));
            soldiers[i].start();
            //if (i==5){
                //soldiers[0].interrupt();
            //}
        }
    }
}

十个士兵线程里公用一个CyclicBarrier,该CyclicBarrier初始化传的10,新建一个线程,启动后,子线程会执行dowork()方法

其实就是休眠一段时间,输出自己完成任务。由于dowork()前有cyclicBarrier.await()。则直到十个线程都创建完毕且执行到

dowork(),这一行时,这时候会触发屏障线程的执行内容,输出所有士兵集合完毕,并将flag设为true。然后十个线程各自做

自己的dowork()休眠一段时间,并输出自己完成工作,这时候又dowork()后也有cyclicBarrier.await()。则必须等十个线程都

做完dowork(),这时又触发屏障线程的工作,输出十个士兵都执行完毕。这就是循环栅栏的可循环使用的点。

下面是执行结果


集合队伍!
士兵0报道!
士兵1报道!
士兵2报道!
士兵3报道!
士兵4报道!
士兵5报道!
士兵6报道!
士兵7报道!
士兵8报道!
士兵9报道!
司令:【士兵 10个,集合完毕!
士兵7任务完成!
士兵8任务完成!
士兵3任务完成!
士兵4任务完成!
士兵9任务完成!
士兵5任务完成!
士兵2任务完成!
士兵6任务完成!
士兵1任务完成!
士兵0任务完成!
司令:【士兵 10个,任务完成!

Process finished with exit code 0

 

<think>我们正在讨论CyclicBarrier,根据引用内容,CyclicBarrier是Java并发库中的一个同步工具,用于让一组线程等待至某个屏障点(barrier)再同时执行,并且可以重用。 概念:CyclicBarrier字面意思是可循环使用(Cyclic)的屏障(Barrier)。它允许一组线程相互等待,直到所有线程都到达屏障点,然后屏障打开,所有线程继续执行。因为可以重用,所以称为循环屏障。 原理:通过引用[1][3]可知,CyclicBarrier内部使用ReentrantLock(独占锁)和Condition来实现线程的阻塞和唤醒。它维护一个计数器,当线程调用await()方法时,计数器减1,然后线程被阻塞(通过Condition.await())。当计数器减到0时,表示所有线程都已到达屏障点,此时通过Condition.signalAll()唤醒所有被阻塞的线程,同时重置计数器(以便重用)或者执行预定义的屏障动作(如果提供了Runnable任务)。 应用场景: 1. 多线程计算数据,最后合并计算结果的场景。 2. 模拟高并发,用于压力测试,模拟一定数量的并发用户同时执行操作。 3. 游戏服务器中,多个玩家都准备好后才开始游戏。 下面我们详细展开:</think>### CyclicBarrier 循环屏障(回环栅栏)详解 #### 一、核心概念 CyclicBarrier 是 Java 并发包 (`java.util.concurrent`) 中的同步工具,其核心设计可概括为: 1. **循环屏障** - 让一组线程在**屏障点(Barrier Point)** 相互等待 - 当所有线程到达屏障时,屏障才会打开,所有线程**同时继续执行** - 如登山队在山脚集合点等待全员到齐后同时出发 2. **可重用性**(核心特性) 屏障释放线程后可**自动重置**,支持重复使用(区别于一次性 CountDownLatch)[^1][^3][^5] ```java CyclicBarrier barrier = new CyclicBarrier(3); // 创建3线程屏障 // 第一次使用:3线程到达屏障后继续执行 // 重置后第二次使用:再次等待3线程到达屏障 ``` 3. **屏障动作** 支持在所有线程通过屏障后执行自定义任务(由最后到达屏障的线程执行)[^2] ```java Runnable barrierAction = () -> System.out.println("全员已就绪!"); CyclicBarrier barrier = new CyclicBarrier(3, barrierAction); ``` #### 二、实现原理(基于源码分析) 通过 **ReentrantLock + Condition** 实现线程协调[^1]: ```mermaid graph LR A[线程1调用await] --> B[获取ReentrantLock锁] B --> C{计数count-1} C -->|count>0| D[condition.await阻塞] C -->|count=0| E[执行屏障动作] E --> F[condition.signalAll唤醒所有线程] F --> G[重置count] ``` 1. **关键成员变量** ```java private final ReentrantLock lock = new ReentrantLock(); private final Condition trip = lock.newCondition(); private int count; // 剩余等待线程数 private final int parties; // 屏障需要的总线程数 private final Runnable barrierCommand; // 屏障动作 ``` 2. **await() 工作流程** ```java public int await() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lock(); try { int index = --count; // 计数减1 if (index == 0) { // 最后一个线程到达 if (barrierCommand != null) barrierCommand.run(); trip.signalAll(); // 唤醒所有线程 reset(); // 重置计数器(count=parties) return 0; } while (index > 0) { // 非最后线程 trip.await(); // 阻塞等待 } return index; } finally { lock.unlock(); } } ``` #### 三、典型应用场景 1. **多阶段计算任务** 分布式计算中合并子任务结果: ```java // 模拟3个线程分别计算数据 CyclicBarrier barrier = new CyclicBarrier(3, () -> { System.out.println("所有子任务完成,开始合并结果"); }); IntStream.range(0,3).forEach(i -> new Thread(() -> { calculateData(i); // 执行计算 barrier.await(); // 等待其他线程 mergeResults(); // 合并结果 }).start()); ``` 2. **压力测试框架** 模拟高并发场景: ```java CyclicBarrier startBarrier = new CyclicBarrier(1000); // 1000并发 ExecutorService pool = Executors.newCachedThreadPool(); for (int i = 0; i < 1000; i++) { pool.execute(() -> { startBarrier.await(); // 等待1000个线程就绪 simulateUserRequest(); // 同时发起请求 }); } ``` 3. **游戏服务器同步** 多玩家回合制游戏: ```java CyclicBarrier roundBarrier = new CyclicBarrier(4, () -> { System.out.println("--- 新回合开始 ---"); }); // 4个玩家线程 players.forEach(player -> new Thread(() -> { while (!gameOver) { playerAction(); // 玩家操作 roundBarrier.await(); // 等待其他玩家 } }).start()); ``` #### 四、与 CountDownLatch 对比 | **特性** | CyclicBarrier | CountDownLatch | |------------------------|--------------------------------|-------------------------| | 重置能力 | ✅ 自动重置(可重用)[^3] | ❌ 一次性 | | 计数方向 | 递减(parties→0) | 递减(设定值→0) | | 阻塞线程 | 所有线程相互等待 | 调用线程等待其他线程 | | 屏障动作 | ✅ 支持自定义任务 | ❌ 不支持 | | 典型场景 | 多阶段并行任务 | 主线程等待子任务初始化 | > ⚠️ **注意**:线程被中断或超时会导致 `BrokenBarrierException`,此时屏障将进入破损状态,需调用 `reset()` 重置[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值