并发工具三之CyclicBarrier

一、CyclicBarrier简介

CyclicBarrier 的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续工作。CyclicBarrier 默认的构造方法是 CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用 await 方法告诉 CyclicBarrier 当前线程已经到达了屏障,然后当前线程被阻塞

二、使用案例

使用场景
当存在需要所有的子任务都完成时,才执行主任务,这个
时候就可以选择使用 CyclicBarrier

案例
定义线程,定义每组运行3个,当未达到3个线程的时候阻塞,当达到3个以后会执行

public class Run extends Thread {

    private CyclicBarrier cyclicBarrier;

    public  Run(CyclicBarrier cyclicBarrier){
        this.cyclicBarrier = cyclicBarrier;
    }

    @Override
    public void run() {
        try {
            TimeUnit.SECONDS.sleep(2);
            System.out.println("线程"+Thread.currentThread().getName()+"准备就绪");
            cyclicBarrier.await();
            System.out.println("线程"+Thread.currentThread().getName()+"开始执行");
        } catch (BrokenBarrierException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

测试代码,定义3个线程运行

public class CyclicBarrierDemo {

    static CyclicBarrier cyclicBarrier = new CyclicBarrier(3);

    public static void main(String[] args) {
        for(int i=0;i<9;i++) {
            new Run(cyclicBarrier).start();
        }
    }
}

打印效果

线程Thread-2准备就绪
线程Thread-1准备就绪
线程Thread-0准备就绪
线程Thread-0开始执行
线程Thread-2开始执行
线程Thread-1开始执行

Process finished with exit code 0

注意点
1)对于指定计数值 parties,若由于某种原因,没有足够的线程调用 CyclicBarrier 的 await,则所有调用 await 的线程都会被阻塞;
2)同样的 CyclicBarrier 也可以调用 await(timeout, unit),设置超时时间,在设定时间内,如果没有足够线程到达,则解除阻塞状态,继续工作;
3)通过 reset 重置计数,会使得进入 await 的线程出现BrokenBarrierException;
4 ) 如 果 采 用 是 CyclicBarrier(int parties, RunnablebarrierAction) 构造方法,执行 barrierAction 操作的是最后一个到达的线程
例如针对上面的实例,我们调整一下代码

public class CyclicBarrierDemo {
	// CyclicBarrier的构造方法可以传入一个回调线程
    static CyclicBarrier cyclicBarrier = new CyclicBarrier(3,new ThreadTest());

    public static void main(String[] args) {
        for(int i=0;i<3;i++) {
            new Run(cyclicBarrier).start();
        }
    }
}
public class ThreadTest extends Thread {

    @Override
    public void run() {
        System.out.println("所有线程准备好,开始执行");
    }
}

再执行,打印效果如下

线程Thread-1准备就绪
线程Thread-3准备就绪
线程Thread-2准备就绪
所有线程准备好,开始执行
线程Thread-2开始执行
线程Thread-3开始执行
线程Thread-1开始执行

Process finished with exit code 0

三、源码分析

CyclicBarrier 相比 CountDownLatch 来说,要简单很多,源码实现是基于 ReentrantLock 和 Condition 的组合使用

    private final ReentrantLock lock = new ReentrantLock();
    /** Condition to wait on until tripped */
    private final Condition trip = lock.newCondition();
    // 记录初始化拦截的线程数
    private final int parties;
    // 记录当前还需要拦截的线程数,初始化值==parties
    private int count;

每次线程调用CyclicBarrier.await()方法,先count-1,然后判断count==0:如果是,则调用Condition.signAll方法唤醒这一组所有线程,并且重新赋值count = parties;
如果否,则调用Condition.await方法阻塞

本文是综合自己的认识和参考各类资料(书本及网上资料)编写,若有侵权请联系作者,所有内容仅代表个人认知观点,如有错误,欢迎校正; 邮箱:[email protected] 博客地址:https://blog.csdn.net/qq_35576976/

你可能感兴趣的:(多线程,java)