CountDownLatch使用簡介與源碼解析

CountDownLatch試用場景

來自于Javadoc的解釋

    A synchronization aid that allows one or more threads to wait until
    a set of operations being performed in other threads completes.

表示一個或者多個線程等待一系列的操作完成。

CountDownLatch是一個同步工具類舷手,用來協(xié)調(diào)多個線程之間的同步着倾,或者說起到線程之間的通信作用拾酝,簡單的說CountDownLatch就是一個計(jì)數(shù)器燕少,能夠使一個或者多個線程等待另外一些線程操作完成之后卡者,再繼續(xù)執(zhí)行,計(jì)數(shù)器的數(shù)量就是線程的數(shù)量客们,當(dāng)每個線程完成自己的任務(wù)之后崇决,計(jì)數(shù)器減一
當(dāng)計(jì)數(shù)器的數(shù)值變?yōu)?的時候,表示所有線程都完成了自己的任務(wù)底挫,等待在CountDownLatch上的線程可以繼續(xù)執(zhí)行自己的任務(wù)恒傻。

例如當(dāng)需要對兩個表進(jìn)行查詢,然后將查詢結(jié)果合并進(jìn)行下一步操作建邓,由于對兩個表的查詢時IO密集型操作盈厘,我們可以考慮使用多線程來提高性能,但是我們需要在兩個查詢操作完成之后通知主線程進(jìn)行合并操作等官边,我們可以用CountDownLatch來完成沸手,代碼如下,通過Thread.sleep(1000)來模擬一個IO耗時的操作

public class CountDownLatchDemo {

    static ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(4, 8, 60,
            TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100), new ThreadPoolExecutor.CallerRunsPolicy());

    public static void main(String[] args){

        CountDownLatch countDownLatch = new CountDownLatch(2);

        poolExecutor.execute(() -> {
            System.out.println("query from table A");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            countDownLatch.countDown();
        });

        poolExecutor.execute(() -> {
            System.out.println("query from table B");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            countDownLatch.countDown();
        });
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("merge result");
    }
}

執(zhí)行結(jié)果為

query from table A
query from table B
merge result

CountDownLatch源碼解析

CountDownLatch通過內(nèi)部類Sync來完成線程間的同步和通信注簿,

    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;

        Sync(int count) {
            setState(count);
        }

        int getCount() {
            return getState();
        }

        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }

        protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
    }

Sync實(shí)現(xiàn)了AQS的兩個模板方法tryAcquireShared和tryReleaseShared契吉,分別表示獲取同步鎖和釋放同步鎖的操作。CountDownLatch中最重要的兩個方法分別為countDown()和await()

countDown方法如下所示

    public void countDown() {
        sync.releaseShared(1);
    }

直接調(diào)用AQS的releaseShared方法來修改同步狀態(tài)诡渴,將state的值減一捐晶。

await方法如下所示

    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

直接調(diào)用AQS的acquireSharedInterruptibly方法,該方法如下

    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

首先調(diào)用tryAcquireShared方法妄辩,該方法若state為0則返回1惑灵,反之返回-1,即若state為0時眼耀,該方法直接返回泣棋,若state不為0,該方法會執(zhí)行doAcquireSharedInterruptibly方法畔塔,

    private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

doAcquireSharedInterruptibly方法會自旋的調(diào)用Sync實(shí)現(xiàn)的tryAcquireShared方法潭辈,直至state為0鸯屿,該方法才能返回,以此來達(dá)到阻塞線程的目的把敢。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末寄摆,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子修赞,更是在濱河造成了極大的恐慌婶恼,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件柏副,死亡現(xiàn)場離奇詭異勾邦,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)割择,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門眷篇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人荔泳,你說我怎么就攤上這事蕉饼。” “怎么了玛歌?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵昧港,是天一觀的道長。 經(jīng)常有香客問我支子,道長创肥,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任值朋,我火速辦了婚禮叹侄,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘吞歼。我一直安慰自己圈膏,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布篙骡。 她就那樣靜靜地躺著稽坤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪糯俗。 梳的紋絲不亂的頭發(fā)上尿褪,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機(jī)與錄音得湘,去河邊找鬼杖玲。 笑死,一個胖子當(dāng)著我的面吹牛淘正,可吹牛的內(nèi)容都是我干的摆马。 我是一名探鬼主播臼闻,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼囤采!你這毒婦竟也來了述呐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤蕉毯,失蹤者是張志新(化名)和其女友劉穎乓搬,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體代虾,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡进肯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了棉磨。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片江掩。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖含蓉,靈堂內(nèi)的尸體忽然破棺而出频敛,到底是詐尸還是另有隱情项郊,我是刑警寧澤馅扣,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站着降,受9級特大地震影響差油,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜任洞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一蓄喇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧交掏,春花似錦妆偏、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至挪鹏,卻和暖如春见秽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背讨盒。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工解取, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人返顺。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓禀苦,卻偏偏與公主長得像蔓肯,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子振乏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345