CyclicBarrier適用于這樣的情況:你希望創(chuàng)建一組任務(wù)煌张,它們并行地執(zhí)行工作骏融,然后在下一個(gè)步驟之前等待萌狂,直到所有任務(wù)都完成茫藏。柵欄和閉鎖的關(guān)鍵區(qū)別在于,所有線程必須同時(shí)到達(dá)柵欄位置凉当,才能繼續(xù)執(zhí)行售葡。
閉鎖用于等待事件挟伙,而柵欄是線程之間彼此等待,等到都到的時(shí)候再?zèng)Q定做下一件事贮缅∏垂可以參考Java并發(fā)工具類(閉鎖CountDownLatch)
拿運(yùn)動(dòng)員的事情舉例筷笨,運(yùn)動(dòng)員們跑到終點(diǎn),互相等待所有人都到達(dá)終點(diǎn)后昌跌,再一起去做喝酒這件事照雁。(運(yùn)動(dòng)員也許不能喝酒的饺蚊,也許大家再跑一輪。)
下面用一個(gè)賽馬程序來(lái)舉例:
package concurrency;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;
class Horse implements Runnable {
private static int counter = 0;
private final int id = counter++;
private int strides = 0;
private static Random rand = new Random(47);
private static CyclicBarrier barrier;
public Horse(CyclicBarrier b) {barrier = b;}
public synchronized int getStrides() {return strides;}
public void run() {
try {
while (!Thread.interrupted()) { //線程內(nèi)不斷循環(huán)
synchronized (this) {
strides += rand.nextInt(3); //每次馬可以走0,1或者2步
}
barrier.await(); //走完后,就等所有其它馬也走完籍凝,才能開始下一回合
}
} catch (InterruptedException e) {
} catch (BrokenBarrierException e) {
throw new RuntimeException(e);
}
}
@Override
public String toString() {
return "Horse " + id + " ";
}
public String tracks() {
StringBuilder s =new StringBuilder();
for(int i = 0; i < getStrides();i++)
s.append("*"); //這里打印每個(gè)馬走的軌跡
s.append(id);
return s.toString();
}
}
public class HorseRace {
static final int FINISH_LINE = 75;
private List<Horse> horses = new ArrayList<Horse>();
private ExecutorService exec = Executors.newCachedThreadPool();
private CyclicBarrier barrier;
public HorseRace(int nHorses, final int pause) {
barrier = new CyclicBarrier(nHorses, new Runnable() {
@Override
public void run() {
StringBuilder s = new StringBuilder();
for (int i = 0; i < FINISH_LINE; i++) {
s.append("="); //打印賽道
}
System.out.println(s);
for (Horse horse : horses) {
System.out.println(horse.tracks()); //打印每匹馬的軌跡
}
for (Horse horse : horses) {
if (horse.getStrides() >= FINISH_LINE) {
System.out.println(horse + "won!"); //每次檢查,如果哪匹馬到終點(diǎn)了退盯,終止所有線程
exec.shutdownNow();
return;
}
}
try {
TimeUnit.MILLISECONDS.sleep(pause); //每走完一輪泻肯,暫停一小會(huì)輸出
} catch (InterruptedException e) {
System.out.println("barrier-action sleep interrupted");
}
}
});
for (int i = 0; i < nHorses; i++) {
Horse horse = new Horse(barrier);
horses.add(horse);
exec.execute(horse); //所有馬的線程開始執(zhí)行
}
}
public static void main(String[] args) {
int nHorses = 7;
int pause = 200;
new HorseRace(nHorses, pause);
}
}
我們假設(shè)賽道長(zhǎng)為75软免,馬每次能走0,1或者2步膏萧,每次走完一輪后蝌衔,互相等待噩斟。一旦所有馬越過(guò)柵欄驮宴,它就會(huì)自動(dòng)為下一回合的比賽做好準(zhǔn)備齐鲤。讀者可以運(yùn)行我的程序给郊,在控制臺(tái)上可以展示出一定的動(dòng)畫效果捧灰。
上面的例子中毛俏,我們向CyclicBarrier提供一個(gè)“柵欄動(dòng)作”,它是一個(gè)Runnable焕蹄,當(dāng)計(jì)數(shù)值到達(dá)0時(shí)自動(dòng)執(zhí)行擦盾,這是CyclicBarrier和CountDownLatch之間的另一個(gè)區(qū)別淌哟。
public CyclicBarrier(int parties, Runnable barrierAction)
除此之外徒仓,CyclicBarrier還提供其他有用的方法,比如getNumberWaiting方法可以獲得CyclicBarrier阻塞的線程數(shù)量症见。isBroken方法用來(lái)知道阻塞的線程是否被中斷谋作。比如以下代碼執(zhí)行完之后會(huì)返回true乎芳。