【轉】Java多執行緒-CyclicBarrier 柵欄
阿新 • • 發佈:2019-01-28
CyclicBarrier 類介紹
CyclicBarrier是一個同步工具類,它允許一組執行緒在到達某個柵欄點(common barrier point)互相等待,發生阻塞,直到最後一個執行緒到達柵欄點,柵欄才會開啟,處於阻塞狀態的執行緒恢復繼續執行.它非常適用於一組執行緒之間必需經常互相等待的情況。CyclicBarrier字面理解是迴圈的柵欄,之所以稱之為迴圈的是因為在等待執行緒釋放後,該柵欄還可以複用。
package com.zhihua.subject;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent .ExecutorService;
import java.util.concurrent.Executors;
/**
* CyclicBarrier同步工具類
* 指定幾個執行緒執行,只有當執行緒數達到指定數量後,
* 執行緒才可以繼續往下執行,不然會一直等待
* @author caizh
*
*/
public class CyclicBarrierTest {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool ();
final CyclicBarrier cb = new CyclicBarrier(3);
for(int i=0;i<3;i++){
Runnable runnable = new Runnable(){
public void run(){
try {
Thread.sleep((long)(Math.random()*10000));
System.out.println ("執行緒" + Thread.currentThread().getName()
+ "即將到達集合地點1,當前已有"
+ (cb.getNumberWaiting()+1) + "個已經到達,"
+ (cb.getNumberWaiting()==2?"都到齊了,繼續走啊":"正在等候"));
// 等待,除非滿足指定執行緒數
cb.await();
Thread.sleep((long)(Math.random()*10000));
System.out.println("執行緒" + Thread.currentThread().getName()
+ "即將到達集合地點2,當前已有" + (cb.getNumberWaiting()+1)
+ "個已經到達," + (cb.getNumberWaiting()==2?"都到齊了,繼續走啊":"正在等候"));
// 等待,除非滿足指定執行緒數
cb.await();
Thread.sleep((long)(Math.random()*10000));
System.out.println("執行緒" + Thread.currentThread().getName()
+ "即將到達集合地點3,當前已有" + (cb.getNumberWaiting() + 1)
+ "個已經到達," + (cb.getNumberWaiting()==2?"都到齊了,繼續走啊":"正在等候"));
cb.await();
} catch (Exception e) {
e.printStackTrace();
}
}
};
service.execute(runnable);
}
service.shutdown();
}
}
建構函式
CyclicBarrier有兩個建構函式:
public CyclicBarrier(int parties)
public CyclicBarrier(int parties, Runnable barrierAction)
引數parties指定執行緒數量,當指定的執行緒值都到達柵欄點時,柵欄開啟,執行緒恢復。需要注意的是,當指定的執行緒數量大於啟動的執行緒數量,比如修改上例中的程式碼,只啟動9個執行緒,那麼所有的執行緒將一直處於等待狀態。第二種情況是指定的執行緒數量小於啟動的執行緒,上例程式碼,啟動11個執行緒,那麼當第十個執行緒到達柵欄點時,那麼這十個執行緒就會恢復繼續執行,而第十一個執行緒將一直處於阻塞狀態。
CyclicBarrier還提供一個更高階的建構函式CyclicBarrier(int parties, Runnable barrierAction),用於線上程到達屏障時,優先執行barrierAction,方便處理更復雜的業務場景。
public class BarrierDemo2 {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(5);
final CyclicBarrier barrier = new CyclicBarrier(5, new Runnable() {
public void run() {
System.out.println("所有執行緒已到達柵欄點");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
for (int i = 0; i < 5; i++) {
service.execute(new Player("玩家" + i, barrier));
}
service.shutdown();
}
}
輸出結果:
玩家4已準備,等待其他玩家準備...
玩家2已準備,等待其他玩家準備...
玩家1已準備,等待其他玩家準備...
玩家0已準備,等待其他玩家準備...
玩家3已準備,等待其他玩家準備...
所有執行緒已到達柵欄點
玩家0已加入遊戲
玩家1已加入遊戲
玩家3已加入遊戲
玩家4已加入遊戲
玩家2已加入遊戲
常用方法介紹
await()
:呼叫該方法會使當前執行緒在柵欄點發生阻塞,直到指定的執行緒數量都達到柵欄點時恢復執行await(long timeout, TimeUnit unit)
:類似於await(),增加了超時時間引數。
當barrier在等待點等待超時時,會丟擲TimeoutException異常,同時,位於該barrier上的其他執行緒也將毀丟擲
BrokenBarrierException異常。這裡說明,barrier上的執行緒要麼同時成功要麼同時失敗,不存在部分成功部分失敗的場景。getNumberWaiting()
:返回當前在柵欄處等待的參與者數目。此方法主要用於除錯和斷言。getParties()
:該方法可以獲得建構函式中指定的需要在柵欄點阻塞的執行緒數量。isBroken()
:查詢此柵欄是否處於損壞狀態。reset()
:將barrier重置為其初始狀態。如果所有參與者目前都在屏障處等待,則它們將返回,同時丟擲一個
BrokenBarrierException