CyclicBarrier 循环栅栏

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章