一、CyclicBarrier能做什么事情
和CountDownLatch一樣,CyclicBarrier也是java.util.concurrent包下的一個類;從類名就可以看出,這是一個可以循環(huán)使用(Cylcic)的屏障(Barrier),所做的事情就是讓一組線程到達一個屏障(同步點)時被阻塞,直到這組線程中的最后一個到達屏障時,屏障才會打開,之前阻塞的線程繼續(xù)運行。過程如下圖所示
上圖中的三個線程中各有一個barrier.await,任何一個線程在運行到barrier.await時都會進入阻塞等待狀態(tài),直到三個線程都到了barrier.await時才從await返回,繼續(xù)向后運行。
二、CyclicBarrier 如何使用
實例化CyclicBarrier對象時通過它的構造函數(shù)設置屏障要攔截的線程(調(diào)用barrier.await的次數(shù))的數(shù)據(jù)量,每個線程通過調(diào)用CyclicBarrier實例的await方法告訴CyclicBarrier我已經(jīng)到達屏障,并將自己阻塞。
此外,如果在構造CyclicBarrier時設置了一個Runnable實現(xiàn),那么最后一個barrier.await 的線程會執(zhí)行這個方法,以完成一些預設工作
CyclicBarrier經(jīng)常用于多線程計算數(shù)據(jù),最后要將計算結果合并的場景。例如一個Excel表記錄了用戶一個季度所有的銀行流水,每個sheet記錄了該用戶每個月的銀行流水情況,要統(tǒng)計該用戶整個季度的銀行流水狀況時,可以先使用多線程統(tǒng)計每個sheet的銀行流水,都執(zhí)行完畢后,使用每個線程的計算結果來計算出該用戶整個季度的銀行流水狀況。
public class CyclicBarrierTest implements Runnable{
/*創(chuàng)建一個CyclicBarrier實例,屏障數(shù)據(jù)設為3,處理完之后執(zhí)行當前類的run方法*/
private CyclicBarrier cb = new CyclicBarrier(3,this);
/*創(chuàng)建線程池,只有三個月的數(shù)據(jù),所以只需三個線程*/
private Executor executor = Executors.newFixedThreadPool(3);
/*創(chuàng)建一個ConcurrentHashMap,用來保存每個sheet計算出的結果*/
private ConcurrentHashMap<String,Integer> sheetBankWaterCount = new ConcurrentHashMap<String, Integer>();
public void count() {
for(int i = 0;i<3;i++){
/*每個線程用來處理單個sheet中的任務*/
executor.execute(new Runnable() {
public void run() {
/*此處加入復雜的邏輯處理代碼*/
sheetBankWaterCount.put(Thread.currentThread().getName(),1);
try {
/*線程完成工作后調(diào)用await 設置屏障*/
cb.await();
}catch (BrokenBarrierException e){
e.printStackTrace();
}catch (InterruptedException e){
e.printStackTrace();
}
}
});
}
}
/*等到所有的*/
public void run() {
int res = 0;
/*根據(jù)之前多線程的結果計算出整個季度的銀行流水*/
for (Map.Entry<String,Integer> sheet: sheetBankWaterCount.entrySet()) {
res += sheet.getValue();
}
sheetBankWaterCount.put("result",res);
/*將結果輸出*/
System.out.println(res);
}
public static void main(String[] args){
CyclicBarrierTest test = new CyclicBarrierTest();
/*注意,此時不需要調(diào)用test.run(),最后一個await方法會調(diào)用run方法*/
test.count();
}
}
三、使用CyclicBarrier時要注意的問題
在線程池中使用CyclicBarrier時一定要注意線程的數(shù)量要多于CyclicBarrier實例中設置的阻塞線程的數(shù)量就會發(fā)生死鎖。
調(diào)用await()方法的次數(shù)一定要等于屏障中設置的阻塞線程的數(shù)量,否則也會死鎖。
四、CyclicBarrier和CountDownLatch的區(qū)別
- 首先二者都能讓一個或多個線程阻塞等待,都可以用在多個線程間的協(xié)調(diào),起到線程同步的作用。但CountDownLatch是多個線程都進行了countDown之后才會觸發(fā)時間,喚醒await在latch上的線程,執(zhí)行完countDown操作之后會繼續(xù)自己線程的工作。而CyclicBarrier是一個柵欄,用于同步所有調(diào)用await方法的線程,等到所有的方法都執(zhí)行了await方法后,所有的線程才會返回各自執(zhí)行自己的工作。
- CountDownLatch計數(shù)器只能使用一次,而CyclicBarrier的計數(shù)器可以調(diào)用 reset() 方法重置,能處理更加復雜的業(yè)務場景。