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

 

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