Java多線程同步工具類之CountDownLatch

在過去咱們實現多線程同步的代碼中,每每使用join()、wait()、notiyAll()等線程間通訊的方式,隨着JUC包的不斷的完善,java爲咱們提供了豐富同步工具類,官方也鼓勵咱們使用工具類來實現多線程的同步,今天咱們就對其中CountDownLatch類的使用與底層實現進行分析與總結。java

1、CountDownLatch使用

CountDownLatch其實能夠看作一個計數器,統計多個線程執行完成的狀況,適用於控制一個或多個線程等待,直到全部線程都執行完畢的場景,相似與Thread.join()的做用。下面咱們經過一個簡單的例子看下CountDownLatch的使用。安全

    public static void main(String[] args) {
        final CountDownLatch countDownLatch = new CountDownLatch(5);

        // 啓動計數線程
        for (int i = 0; i < 5; i++) {
            new CountDownLatchThread(i, countDownLatch).start();
        }

        // 啓動等待線程
        for (int i = 0; i < 5; i++) {
            new Thread() {
                public void run() {

                    try {
                        countDownLatch.await();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }

                    System.out.println("計數完畢了," + Thread.currentThread().getName() + "等待線程執行");

                }
            }.start();
        }

    }

計數線程代碼:微信

public class CountDownLatchThread extends Thread {

    private CountDownLatch countDownLatch;

    private int name;

    private int count;

    public CountDownLatchThread(int name, CountDownLatch countDownLatch) {
        this.name = name;
        this.countDownLatch = countDownLatch;
        this.count = 0;
    }

    public void run() {
        try {
            for (int i = 0; i < 10; i++) {
                Thread.sleep(100);
                count++;
            }
            System.out.println(name + "號線程--" + Thread.currentThread().getName() + "--計數完成了");
            countDownLatch.countDown();

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

輸出結果:多線程

1號線程--Thread-1--計數完成了
0號線程--Thread-0--計數完成了
4號線程--Thread-4--計數完成了
2號線程--Thread-2--計數完成了
3號線程--Thread-3--計數完成了
計數完畢了,Thread-5等待線程執行
計數完畢了,Thread-6等待線程執行
計數完畢了,Thread-7等待線程執行
計數完畢了,Thread-8等待線程執行
計數完畢了,Thread-9等待線程執行

經過上面的例子能夠看到,利用CountDownLatch的countDown方法與await()方法,咱們能夠同步計數線程與等待線程,使等待線程在全部計數線程完成後再開始運行。函數

2、CountDownLatch源碼分析

接下來咱們對countDownLatch內部源碼進行一下分析。工具

一、CountDownLatch的構造。

首先看下CountDownLatch的構造函數源碼分析

    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }

CountDownLatch的構造函數會接收一個count值作爲計數器,也就是若是你須要等待N個線程執行結束,那這裏就傳入N。同時CountDownLatch會實例化一個Sync對象,這個Sync實際上是CountDownLatch內部定義的一個繼承自AbstractQueuedSynchronizer的實現類,因此CountDownLatch提供的同步和其餘功能都是圍繞Sync這個子類實現的,也就是基於AbstractQueuedSynchronizer類來實現的。ui

咱們來看下Sync這個類的定義this

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

        /**
        一、設置AbstractQueuedSynchronizer中同步狀態的值state,也就是計數器的值。
        二、這個值volatile變量,必須保證線程間的可見性;
        **/
        Sync(int count) {
            setState(count);
        }

        int getCount() {
            return getState();
        }

        //獲取同步狀態的值
        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }

        protected boolean tryReleaseShared(int releases) {
            // 經過CAS操做改變同步狀態值,保證同步狀態的值state的線程安全。
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
    }

二、countDown方法

首先咱們看下countDown方法的源碼spa

    
    public void countDown() {
       //改變同步狀態值,線程執行完成時計數器減一
        sync.releaseShared(1);
    }

 AbstractQueuedSynchronizer類中releaseShared() 方法的源碼

    public final boolean releaseShared(int arg) {
        // CountDownLatch定義的子類Sync實現,經過CAS操做改變State的值
        if (tryReleaseShared(arg)) {
            //State以遞減爲0,表明着全部執行線程執行完畢,共享模式下釋放鎖,那麼等待線程就可以拿到鎖往下執行。
            doReleaseShared();
            return true;
        }
        return false;
    }

 

當調用CountDownLatch的countDown方法時,就會執行計數器進行減一操做,直到全部線程所有執行完畢,計算器爲0時喚醒等待線程。

AbstractQueuedSynchronizer中doReleaseShared方法是執行共享模式下釋放鎖的操做,從而讓等待線程獲取鎖,繼續向下執行。

三、await方法

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

 AbstractQueuedSynchronizer類中acquireSharedInterruptibly() 方法的源碼

    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        //獲取同步狀態值
        if (tryAcquireShared(arg) < 0)
            //同步狀態值即計數器的值不爲0,等待線程共享模式下嘗試獲取鎖,獲取不到鎖的話進入阻塞
            doAcquireSharedInterruptibly(arg);
    }

await方法的實現也很明確,首頁獲取同步狀態也就是計數器的值,若是爲0即全部線程執行完畢返回1,不然返回-1的話,等待線程在共享模式下嘗試獲取鎖,獲取不到鎖的話進入阻塞;

AbstractQueuedSynchronizer中doAcquireSharedInterruptibly方法是執行共享模式下獲取鎖的操做;

3、總結

經過上面分析能夠看到CountDownLatch是基於AbstractQueuedSynchronizer類實現的,一個很是實用的多線程控制工具類,它相似與一個計數器用來控制指定的線程等待,直到計數器歸零。以上咱們對CountDownLatch類的使用與核心方法的源碼進入了必定的分析,其中若有不足與不正確的地方還望指出與海涵。

 

關注微信公衆號,查看更多技術文章。

相關文章
相關標籤/搜索