天天看點

Java并發程式設計之倒計鎖 CountDownLatch

Java并發程式設計之倒計鎖 CountDownLatch

作者: 西魏陶淵明

部落格: ​​https://blog.springlearn.cn/​​

西魏陶淵明

莫笑少年江湖夢,誰不少年夢江湖

上文我們知道了 ​

​Semaphore​

​​ 信号量的用法,那麼這一篇基本不用學了。因為原理基本上是一樣的。

但是用法不太一樣。

​Semaphore​

​​ 是擷取到資源就執行,擷取不到資源就等待。

​​

​CountDownLatch​

​​ 跟 ​

​Semaphore​

​ 正好相反。

CountDownLatch#await() 可以了解為擷取不到資源,就等待。這麼說不太好了解,直接看源碼吧。

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) {
            // 擷取資源,await就是調用這個方法。當tryReleaseShared沒有進行扣減之前。
            // 這裡一直都是-1。而-1就是擷取不到資源進行等待
            return (getState() == 0) ? 1 : -1;
        }

        protected boolean tryReleaseShared(int releases) {
            // countDown就調用這個方法,進行扣減1
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
    }      

常用用法

注意:

CountDownLatch不可重複使用,當計數器減少到0之後,就廢了,無法繼續使用了。

  • CountDownLatch是在java1.5被引入的,跟它一起被引入的并發工具類還有CyclicBarrier、Semaphore、ConcurrentHashMap和BlockingQueue,它們都存在于java.util.concurrent包下。CountDownLatch這個類能夠使一個線程等待其他線程完成各自的工作後再執行。例如,應用程式的主線程希望在負責啟動架構服務的線程已經啟動所有的架構服務之後再執行。
  • CountDownLatch是通過一個計數器來實作的,計數器的初始值為線程的數量。每當一個線程完成了自己的任務後,計數器的值就會減1。當計數器值到達0時,它表示所有的線程已經完成了任務,然後在閉鎖上等待的線程就可以恢複執行任務。

CountDownLatch 存在的意義

讓主線程阻塞,等待線程結束後在運作

直譯過來就是倒計數(CountDown)門闩(Latch)。倒計數不用說,門闩的意思顧名思義就是阻止前進。在這裡就是指 CountDownLatch.await() 方法在倒計數為0之前會阻塞目前線程。

實作

代碼解釋

public void test()throws Exception{
        int pagecount=3;
        ExecutorService executors = Executors.newFixedThreadPool(pagecount);
        CountDownLatch countDownLatch = new CountDownLatch(pagecount);
        for (int i = 0; i < pagecount; i++) {
            // 啟動線程抓取
            executors
                    .execute(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println(Thread.currentThread().getName());
                            countDownLatch.countDown();
                        }
                    });
        }
        countDownLatch.await();//主線程阻塞在這裡,等到線程結束,
        //然後關閉線程池
        executors.shutdown();

    }      

最大缺點