Java并發(fā)編程之CyclicBarrier使用指南

一、CyclicBarrier能做什么事情

和CountDownLatch一樣,CyclicBarrier也是java.util.concurrent包下的一個類;從類名就可以看出,這是一個可以循環(huán)使用(Cylcic)的屏障(Barrier),所做的事情就是讓一組線程到達一個屏障(同步點)時被阻塞,直到這組線程中的最后一個到達屏障時,屏障才會打開,之前阻塞的線程繼續(xù)運行。過程如下圖所示

CyclicBarrier運行圖

上圖中的三個線程中各有一個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ū)別

  1. 首先二者都能讓一個或多個線程阻塞等待,都可以用在多個線程間的協(xié)調(diào),起到線程同步的作用。但CountDownLatch是多個線程都進行了countDown之后才會觸發(fā)時間,喚醒await在latch上的線程,執(zhí)行完countDown操作之后會繼續(xù)自己線程的工作。而CyclicBarrier是一個柵欄,用于同步所有調(diào)用await方法的線程,等到所有的方法都執(zhí)行了await方法后,所有的線程才會返回各自執(zhí)行自己的工作。
  2. CountDownLatch計數(shù)器只能使用一次,而CyclicBarrier的計數(shù)器可以調(diào)用 reset() 方法重置,能處理更加復雜的業(yè)務場景。

上一篇-java并發(fā)之CountDownLatch使用方法

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 莫在該拼搏的時候選擇安逸,莫在后悔中度過一生。
    1b811b963cdd閱讀 198評論 0 0
  • 又是好久沒寫了!好累的,有時候也不知道寫什么,但是我覺得我們是鬧別扭了嗎?上周六,你和朋友出去玩,什么也沒和我說,...
    LiuWaiting閱讀 193評論 0 0
  • 我的寶貝盛開是個愛笑的孩子。我爸說,這個孩子整天一副甜不梭的臉,這讓我想起朋友也是這么說我的。有人說,愛笑的孩子運...
    毛小生閱讀 280評論 3 1
  • 每次夜班之前都有夜班綜合癥
    小諸諸閱讀 223評論 0 0

友情鏈接更多精彩內(nèi)容