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