Java—CountDownLatch使用詳解


CountDownLatch介紹

CountDownLatch概述

  1. CountDownLatch一般用作多線程倒計時計數器,強制它們等待其他一組(CountDownLatch的初始化決定)任務執行完成。
  2. 有一點要說明的是CountDownLatch初始化后計數器值遞減到0的時候,不能再復原的,這一點區別於SemaphoreSemaphore是可以通過release操作恢復信號量的。

CountDownLatch使用原理

使用原理

  1. 創建CountDownLatch並設置計數器值。
  2. 啟動多線程並且調用CountDownLatch實例的countDown()方法。
  3. 主線程調用 await() 方法,這樣主線程的操作就會在這個方法上阻塞,直到其他線程完成各自的任務,count值為0,停止阻塞,主線程繼續執行。

使用模板

public class CountDownLatchModule {

    //線程數
    private static int N = 10;

    // 單位:min
    private static int countDownLatchTimeout = 5;

    public static void main(String[] args) {
        //創建CountDownLatch並設置計數值,該count值可以根據線程數的需要設置
        CountDownLatch countDownLatch = new CountDownLatch(N);

		//創建線程池
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

        for (int i = 0; i < N; i++) {
            cachedThreadPool.execute(() ->{
                try {
                    System.out.println(Thread.currentThread().getName() + " do something!");
                } catch (Exception e) {
                    System.out.println("Exception: do something exception");
                } finally {
                    //該線程執行完畢-1
                    countDownLatch.countDown();
                }
            });
        }

        System.out.println("main thread do something-1");
        try {
            countDownLatch.await(countDownLatchTimeout, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            System.out.println("Exception: await interrupted exception");
        } finally {
            System.out.println("countDownLatch: " + countDownLatch.toString());
        }
        System.out.println("main thread do something-2");
        //若需要停止線程池可關閉;
//        cachedThreadPool.shutdown();

    }

運行結果:

main thread do something-1
pool-1-thread-1 do something!
pool-1-thread-2 do something!
pool-1-thread-3 do something!
pool-1-thread-5 do something!
pool-1-thread-6 do something!
pool-1-thread-7 do something!
pool-1-thread-8 do something!
pool-1-thread-4 do something!
pool-1-thread-9 do something!
pool-1-thread-10 do something!
countDownLatch: java.util.concurrent.CountDownLatch@76fb509a[Count = 0]
main thread do something-2

CountDownLatch常用方法

在這里插入圖片描述

  • public void await() throws InterruptedException:調用await()方法的線程會被掛起,等待直到count值為0再繼續執行。
  • public boolean await(long timeout, TimeUnit unit) throws InterruptedException:同await(),若等待timeout時長后,count值還是沒有變為0,不再等待,繼續執行。時間單位如下常用的毫秒、天、小時、微秒、分鍾、納秒、秒。
    在這里插入圖片描述
  • public void countDown(): count值遞減1.
  • public long getCount():獲取當前count值。
  • public String toString():重寫了toString()方法,多打印了count值,具體參考源碼。

CountDownLatch使用場景

一個程序中有N個任務在執行,我們可以創建值為N的CountDownLatch,當每個任務完成后,調用一下countDown()方法進行遞減count值,再在主線程中使用await()方法等待任務執行完成,主線程繼續執行。

CountDownLatch源碼

構造方法源碼

    /**
     * Constructs a {@code CountDownLatch} initialized with the given count.
     *
     * @param count the number of times {@link #countDown} must be invoked
     *        before threads can pass through {@link #await}
     * @throws IllegalArgumentException if {@code count} is negative
     */
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }

toString()方法源碼

    /**
     * Returns a string identifying this latch, as well as its state.
     * The state, in brackets, includes the String {@code "Count ="}
     * followed by the current count.
     *
     * @return a string identifying this latch, as well as its state
     */
    public String toString() {
        return super.toString() + "[Count = " + sync.getCount() + "]";
    }

CountDownLatch示例

作為線程啟動信號

代碼

public class CountDownLatchTest {

    /**
     * a start signal that prevents any worker from proceeding
     * until the driver is ready for them to proceed;
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch startSignal = new CountDownLatch(1);
        CountDownLatch doneSignal = new CountDownLatch(10);
        for (int i = 0; i < 10; i++) {
            // create and start threads
            new Thread(new Worker(startSignal, doneSignal)).start();
        }
        // don't let run yet
        System.out.println("do something else 1");
        // let all threads proceed
        startSignal.countDown();
        System.out.println("do something else 2");
        // wait for all to finish
        doneSignal.await();
        System.out.println("wait for all to finsh");
    }

    static class Worker implements Runnable{

        private final CountDownLatch startSignal;
        private final CountDownLatch doneSignal;

        Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
            this.startSignal = startSignal;
            this.doneSignal = doneSignal;
        }

        @Override
        public void run() {
            try {
                startSignal.await();
                doWork();
                doneSignal.countDown();
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }

        void doWork() {
            System.out.println("do work!");
        }
    }

}

運行結果

do something else 1
do something else 2
do work!
do work!
do work!
do work!
do work!
do work!
do work!
do work!
do work!
do work!
wait for all to finsh

從運行結果可以看出:

  1. 主線程先打印do something else 1do something else 2。因為startSignal.countDown();完后,count才為0,子線程才能打印。
  2. 因為startSignal.await();是在子線程內,所有子線程都等待startSignal.countDown()執行后才能打印do work!
  3. doneSignal.await();等待所有子線程執行后,每次都doneSignal.countDown(),最后count為0,主線程才執行打印wait for all to finsh

作為線程等待完成信號

代碼

public class CountDownLatchTest2 {

    /**
     * a completion signal that allows the driver to wait
     * until all workers have completed.
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch doneSignal = new CountDownLatch(5);
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            // create and start threads
            cachedThreadPool.execute(new Worker(doneSignal, i));
        }
        // don't let run yet
        System.out.println("do something else 1");
        // wait for all to finish
        doneSignal.await();
        System.out.println("===========================count: " + doneSignal.getCount());
        System.out.println("do something else 2");
        cachedThreadPool.shutdown();
    }

    static class Worker implements Runnable{

        private final CountDownLatch doneSignal;
        private final int i;

        Worker(CountDownLatch doneSignal, int i) {
            this.doneSignal = doneSignal;
            this.i = i;
        }

        @Override
        public void run() {
            try {
                doWork();
                doneSignal.countDown();
                System.out.println("i = " + i + ", " + doneSignal.toString());
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }

        void doWork() {
            System.out.println("do work!");
        }
    }

}

運行結果

do something else 1
do work!
i = 0, java.util.concurrent.CountDownLatch@128abd43[Count = 4]
do work!
i = 1, java.util.concurrent.CountDownLatch@128abd43[Count = 3]
do work!
i = 2, java.util.concurrent.CountDownLatch@128abd43[Count = 2]
do work!
i = 3, java.util.concurrent.CountDownLatch@128abd43[Count = 1]
do work!
i = 4, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
===========================count: 0
do something else 2
do work!
i = 5, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
do work!
i = 6, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
do work!
i = 7, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
do work!
i = 8, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
do work!
i = 9, java.util.concurrent.CountDownLatch@128abd43[Count = 0]

從運行結果可以看出,主線程是等待其他線程運行了5次結束后就打印了do something else 2信息,因為CountDownLatch數值為5。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM