并发之CyclicBarrier实现与探索

CyclicBarrier,这时JDK1.5的java.util.concurrent并发包中提供的一个并发工具类。
从字面意思来理解的话,cyclic是循环的意思,Barrier是屏障。是的,这个工具类同样是用于一个或者多个线程同时完成后才能继续执行往后的步骤。

有一个公共的屏障点的概念,只有所有的线程都到达屏障点之后才可以继续执行。这里我们可以对比一下,他跟CountDownLatch的用法有一些相似的地方。但是我们可以发现以下一些区别:

相同点:
两个都可以同来实现一个或者多个线程等待其他线程全都执行完成一定的操作之后,才开始接着往下执行。这里我查找资料说: CountDownLatch比较关注等待其他线程完成一些事之后才能继续执行,而CyclicBarrier其实是比较关注于所有的线程全部完成某些任务,才能一起执行下去。

区别:
1.从字面意思来看,CyclicBarrier中的Cyclic有循环的意思,所以CyclicBarrier是可以支持循环的实现所有的线程要一起做任务的目标的。而不是像CountDownLatch一样,只支持一次线程同步堵塞的意思。
2.从底层实现来看,CyclicBarrier底层主要是采用ReentrantLock+Condition实现的,而CountDownLatch内部采用共享锁来实现。CountDownLatch内部是进行减法来判断,而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。

看下代码的演示:

这个自己也手敲了一遍
public void test01() throws InterruptedException {

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

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

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

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

    public static void main(String[] args) throws InterruptedException {
        TestCyclicBarrier c=new TestCyclicBarrier();
        c.test01();
    }

结果:


并发之CyclicBarrier实现与探索_第1张图片

然后再来写一组模拟多线程分组计算
有一个大小为50000的随机数组,用五个线程分别计算10000个元素的和,在将结果进行合并。

public class TestCyclicBarrierCalculate {

    public static void main(String[] args) {

        //数组大小
        int size = 50000;
        //定义数组
        int[] numbers = new int[size];
        Random random = new Random();
        //随机初始化数组
        for (int i = 0; i < size; i++) {
            numbers[i] = random.nextInt(10);
        }

        //单线程计算结果
        System.out.println();
        int sum = 0;
        for (int i = 0; i < size; i++) {
            sum += numbers[i];
        }
        System.out.println("单线程计算结果:" + sum);

        ExecutorService executorService = Executors.newCachedThreadPool();
        //定义五个Future去保存子数组计算结果
        final int [] result=new int[5];

        CyclicBarrier barrier = new CyclicBarrier(5, () -> {
            int sums = 0;
            for (int i = 0; i < 5; i++) {
                sums += result[i];
            }
            System.out.println("多线程计算结果为:"+sums);
        });

        int lengths=10000;
        //定义五个线程去计算
        for(int i=0;i<5;i++) {

            int[] range = Arrays.copyOfRange(numbers, i * lengths, (i + 1) * lengths);
            int finalI = i;
            executorService.execute(() -> {
                //临时计算结果
                int temp = 0;
                for (int j = 0; j < 10000; j++) {
                    temp += range[j];
                }
                System.out.println("第N个多线程计算结果为:"+temp);
                result[finalI] = temp;
                try {
                    //暂停,等待其他线程的计算结果
                    barrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            });
        }
        executorService.shutdown();
    }
}

结果:


并发之CyclicBarrier实现与探索_第2张图片
image.png

喜欢可以关注我哦

你可能感兴趣的:(并发之CyclicBarrier实现与探索)