JUC源碼分析-JUC鎖(四):CyclicBarrier

1.概述

CyclicBarrier是一個同步輔助類贞间,允許一組線程互相等待,直到到達(dá)某個公共屏障點(diǎn) (common barrier point)继找。如果一個程序中有固定的線程數(shù)喘帚,并且線程之間需要相互等待户辱,這時候CyclicBarrier是一個很好的選擇。之所以叫它c(diǎn)yclic疏哗,是因?yàn)樵卺尫诺却€程之后呛讲,它可以被重用。還是那句話返奉,開始之前你需要先了解AQS的實(shí)現(xiàn)機(jī)制贝搁。

CyclicBarrier運(yùn)行機(jī)制

CountDownLatch和CyclicBarrier的區(qū)別:

  1. CountDownLatch的作用是允許1或N個線程等待其他線程完成執(zhí)行;而CyclicBarrier則是允許N個線程相互等待衡瓶。
  2. CountDownLatch的計數(shù)器無法被重置徘公;CyclicBarrier的計數(shù)器可以被重置后使用,因此它被稱為是循環(huán)的barrier哮针。

2. 函數(shù)列表和核心參數(shù)

//-------------------------核心參數(shù)------------------------------
// 內(nèi)部類
private static class Generation {
    boolean broken = false;
}
/** 守護(hù)barrier入口的鎖 */
private final ReentrantLock lock = new ReentrantLock();
/** 等待條件关面,直到所有線程到達(dá)barrier */
private final Condition trip = lock.newCondition();
/** 要屏障的線程數(shù) */
private final int parties;
/* 當(dāng)線程都到達(dá)barrier,運(yùn)行的 barrierCommand*/
private final Runnable barrierCommand;
/** The current generation */
private Generation generation = new Generation();
//等待到達(dá)barrier的參與線程數(shù)量十厢,count=0 -> tripped
private int count;

//-------------------------函數(shù)列表------------------------------
//構(gòu)造函數(shù)等太,指定參與線程數(shù)
public CyclicBarrier(int parties)
//構(gòu)造函數(shù),指定參與線程數(shù)蛮放,并在所有線程到達(dá)barrier之后執(zhí)行給定的barrierAction邏輯
public CyclicBarrier(int parties, Runnable barrierAction);
//等待所有的參與者到達(dá)barrier
public int await();
//等待所有的參與者到達(dá)barrier缩抡,或等待給定的時間
public int await(long timeout, TimeUnit unit);
//獲取參與等待到達(dá)barrier的線程數(shù)
public int getParties();
//查詢barrier是否處于broken狀態(tài)
public boolean isBroken();
//重置barrier為初始狀態(tài)
public void reset();
//返回等待barrier的線程數(shù)量
public int getNumberWaiting();
  1. Generation:每個使用中的barrier都表示為一個generation實(shí)例。當(dāng)barrier觸發(fā)trip條件或重置時generation隨之改變包颁。使用barrier時有很多generation與線程關(guān)聯(lián)瞻想,由于不確定性的方式,鎖可能分配給等待的線程娩嚼。但是在同一時間只有一個是活躍的generation(通過count變量確定)蘑险,并且其余的要么被銷毀,要么被trip條件等待岳悟。如果有一個中斷佃迄,但沒有隨后的重置泼差,就不需要有活躍的generationCyclicBarrier的可重用特性就是通過Generation來實(shí)現(xiàn)呵俏,每一次觸發(fā)tripped都會new一個新的Generation堆缘。
  2. barrierCommand:CyclicBarrier的另一個特性是在所有參與線程到達(dá)barrier觸發(fā)一個自定義函數(shù),這個函數(shù)就是barrierCommand普碎,在CyclicBarrier的構(gòu)造函數(shù)中初始化吼肥。

3. 使用示例

public class CyclicBarrierTest2 {
    public static int SIZE = 5;
    private static CyclicBarrier cyclicBarrier;

    public static void main(String[] args) {
        cyclicBarrier = new CyclicBarrier(SIZE, () -> {
            //觸發(fā)barrier時執(zhí)行的函數(shù)
            System.out.println(Thread.currentThread().getName() + " barrierAction finish");
        });
        for (int i=0;i<SIZE;i++){

            new Thread(new InnerThread(),"Thread"+i).start();
        }

    }

    static class InnerThread implements Runnable{
        @Override
        public void run() {
            try {
                System.out.println(Thread.currentThread().getName() + " wait for barrier");
                cyclicBarrier.await();
                TimeUnit.SECONDS.sleep(1);

                System.out.println(Thread.currentThread().getName() + " continued");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }
}

4. 源碼解析

4.1 await()

public int await() throws InterruptedException, BrokenBarrierException {
    try {
        return dowait(false, 0L);
    } catch (TimeoutException toe) {
        throw new Error(toe); // cannot happen
    }
}
//await實(shí)現(xiàn)
private int dowait(boolean timed, long nanos)
    throws InterruptedException, BrokenBarrierException,
           TimeoutException {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        //當(dāng)前generation
        final Generation g = generation;

        if (g.broken)
            throw new BrokenBarrierException();

        if (Thread.interrupted()) {
            breakBarrier();//線程被中斷,終止Barrier随常,喚醒所有等待線程
            throw new InterruptedException();
        }

        int index = --count;
        if (index == 0) {  // tripped
            boolean ranAction = false;
            try {
                final Runnable command = barrierCommand;
                if (command != null)
                    command.run();//如果有barrierCommand潜沦,在所有parties到達(dá)之后運(yùn)行它
                ranAction = true;
                //更新barrier狀態(tài)并喚醒所有線程
                nextGeneration();
                return 0;
            } finally {
                if (!ranAction)
                    breakBarrier();
            }
        }

        // loop until tripped, broken, interrupted, or timed out
        //自旋等待 所有parties到達(dá) | generation被銷毀 | 線程中斷 | 超時
        for (;;) {
            try {
                if (!timed)
                    trip.await();
                else if (nanos > 0L)
                    nanos = trip.awaitNanos(nanos);
            } catch (InterruptedException ie) {
                if (g == generation && ! g.broken) {
                    breakBarrier();
                    throw ie;
                } else {
                    // We're about to finish waiting even if we had not
                    // been interrupted, so this interrupt is deemed to
                    // "belong" to subsequent execution.
                    Thread.currentThread().interrupt();
                }
            }

            if (g.broken)
                throw new BrokenBarrierException();

            if (g != generation)
                return index;

            if (timed && nanos <= 0L) {
                breakBarrier();//超時,銷毀當(dāng)前barrier
                throw new TimeoutException();
            }
        }
    } finally {
        lock.unlock();
    }
}

說明:dowait()await()的實(shí)現(xiàn)函數(shù)绪氛,它的作用就是讓當(dāng)前線程阻塞唆鸡,直到“有parties個線程到達(dá)barrier” 或 “當(dāng)前線程被中斷” 或 “超時”這3者之一發(fā)生,當(dāng)前線程才繼續(xù)執(zhí)行枣察。當(dāng)所有parties到達(dá)barrier(count=0)争占,如果barrierCommand不為空,則執(zhí)行barrierCommand序目。然后調(diào)用nextGeneration()進(jìn)行換代操作臂痕。
for(;;)自旋中。timed是用來表示當(dāng)前是不是“超時等待”線程猿涨。如果不是握童,則通過trip.await()進(jìn)行等待;否則叛赚,調(diào)用awaitNanos()進(jìn)行超時等待澡绩。

小結(jié)

CyclicBarrier主要通過獨(dú)占鎖ReentrantLockCondition配合實(shí)現(xiàn)。類本身實(shí)現(xiàn)很簡單俺附,重點(diǎn)是分清CyclicBarrierCountDownLatch的用法及區(qū)別肥卡,還有在jdk1.7新增的另外一個與它們相似的同步鎖Phaser,在后面文章中會詳細(xì)講解事镣。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末步鉴,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子璃哟,更是在濱河造成了極大的恐慌氛琢,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件随闪,死亡現(xiàn)場離奇詭異阳似,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蕴掏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進(jìn)店門障般,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人盛杰,你說我怎么就攤上這事挽荡。” “怎么了即供?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵定拟,是天一觀的道長。 經(jīng)常有香客問我逗嫡,道長青自,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任驱证,我火速辦了婚禮延窜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘抹锄。我一直安慰自己逆瑞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布伙单。 她就那樣靜靜地躺著获高,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吻育。 梳的紋絲不亂的頭發(fā)上念秧,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天,我揣著相機(jī)與錄音布疼,去河邊找鬼摊趾。 笑死,一個胖子當(dāng)著我的面吹牛缎除,可吹牛的內(nèi)容都是我干的严就。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼器罐,長吁一口氣:“原來是場噩夢啊……” “哼梢为!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起轰坊,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤铸董,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后肴沫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體粟害,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年颤芬,在試婚紗的時候發(fā)現(xiàn)自己被綠了悲幅。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片套鹅。...
    茶點(diǎn)故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖汰具,靈堂內(nèi)的尸體忽然破棺而出卓鹿,到底是詐尸還是另有隱情,我是刑警寧澤留荔,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布吟孙,位于F島的核電站,受9級特大地震影響聚蝶,放射性物質(zhì)發(fā)生泄漏杰妓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一碘勉、第九天 我趴在偏房一處隱蔽的房頂上張望巷挥。 院中可真熱鬧,春花似錦验靡、人聲如沸句各。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽凿宾。三九已至,卻和暖如春兼蕊,著一層夾襖步出監(jiān)牢的瞬間初厚,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工孙技, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留产禾,地道東北人。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓牵啦,卻偏偏與公主長得像亚情,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子哈雏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評論 2 354

推薦閱讀更多精彩內(nèi)容