java并發(fā)之CountDownLatch使用指南

一、CountDownLatch能做什么

CountDownLatch是java.util.concurrent包中的一個類,它主要用來協(xié)調(diào)多個線程之間的同步,起到一個同步器的作用??偟膩碚f,CountDownLatch讓一個或多個線程在運(yùn)行過程中的某個時間點(diǎn)能停下來等待其他的一些線程完成某些任務(wù)后再繼續(xù)運(yùn)行。

類似的任務(wù)可以使用線程的 join() 方法實(shí)現(xiàn):在等待時間點(diǎn)調(diào)用其他線程的 join() 方法,當(dāng)前線程就會等待join線程執(zhí)行完之后才繼續(xù)執(zhí)行,但 CountDownLatch 實(shí)現(xiàn)更加簡單,并且比 join 的功能更多。

二、CountDownLatch怎么用

CountDownLatch的構(gòu)造函數(shù)接收一個 int 型參數(shù)作為計(jì)數(shù)器,例如想讓N任務(wù)完成之后才能繼續(xù)執(zhí)行,創(chuàng)建CountDownLatch時傳入?yún)?shù)N;

需要等待的線程會調(diào)用CountDownLatch的await方法,該線程就會阻塞直到計(jì)數(shù)器的值減為0,而達(dá)到自己預(yù)期的線程會調(diào)用CountDownLatch的countDown()方法,計(jì)數(shù)器N的值減1。由于countDown方法可以在任何地方調(diào)用,所以計(jì)數(shù)器N既可以代表N個線程,也可以是一個線程的N個執(zhí)行步驟。代表多個線程時,只需要將這個CountDownLatch的引用傳到線程里面即可。

N的值為1時,退化為單一事件,即由一個線程來通知其他線程,效果等同于對象的wait和notifyAll;N為0時,調(diào)用await方法不會阻塞當(dāng)前線程。

image

上圖中,等待線程為4和5,計(jì)數(shù)器的值初始化為3,當(dāng)線程1,2,3都完成調(diào)用countDown()方法后,線程4,5才會從await()方法返回,繼續(xù)執(zhí)行后面的代碼。

綜上,使用CountDownLatch有三個主要步驟
首先創(chuàng)建CountDownLatch實(shí)例時通過構(gòu)造函數(shù)指定計(jì)數(shù)器的值N;
然后需要等待的線程調(diào)用CountDownLatch實(shí)例的await方法,等待計(jì)數(shù)器的值將為0;
被等待的線程在任務(wù)完成時調(diào)用CountDownLatch實(shí)例的countDown方法,計(jì)數(shù)器的值減一。

一個例子:有三個線程解析表格中的數(shù)據(jù),主線程需要等到表格解析完成后才執(zhí)行后面的操作

public class CountDownLatchTest {
    public static void main(String[] args) throws Exception{

        /*創(chuàng)建CountDownLatch實(shí)例,計(jì)數(shù)器的值初始化為3*/
        final CountDownLatch downLatch = new CountDownLatch(3);

        /*創(chuàng)建三個線程,每個線程等待1s,表示執(zhí)行比較耗時的任務(wù)*/
        for(int i = 0;i < 3;i++){
            final int num = i;
            new Thread(new Runnable() {
                public void run() {
                    try {
                        Thread.sleep(1000);

                    }catch (InterruptedException e){
                        e.printStackTrace();

                    }

                    System.out.println(String.format("thread %d has finished",num));

                    /*任務(wù)完成后調(diào)用CountDownLatch的countDown()方法*/
                    downLatch.countDown();
                }
            }).start();
        }

        /*主線程調(diào)用await()方法,等到其他三個線程執(zhí)行完后才繼續(xù)執(zhí)行*/
        downLatch.await();

        System.out.print("all threads have finished,main thread will continue run");
    }
}

運(yùn)行結(jié)果:

image

三、CountDown 源碼分析

分析CountDownLatch的源碼我們可以知道,它是使用了一個內(nèi)部同步器AQS來實(shí)現(xiàn)屏蔽功能的。只有當(dāng)count的值為零時,同步器的tryAcquireShared的結(jié)果為1,其他時候都是-1。詳細(xì)代碼如下:

     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;
            }
        }
    }
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }

四、CountDownLatch 的不足

CountDownLatch是一次性的,不可能重新初始化或者修改其內(nèi)部計(jì)數(shù)器的值,當(dāng)CountDownLatch使用完畢后,它不能再次被使用。

下一篇-Java并發(fā)編程之CyclicBarrier使用方法

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

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