基於java8分析CountDownLatch源碼

ReentrantLock和ReentrantReadWriteLock都實現interface接口,內部又繼承AbstractQueuedSynchronizer。而CountDownLatch是JUC中提供的一個多線程間協同的工具類,並沒有實現interface接口,只在內部繼承實現AQS。

CountDownLatch主要處理:主線程將某個大任務切分成數個小任務,由多個線程處理小任務,而主線程阻塞直到所有任務完成後,主線程再處理接下來的任務。即:處理一個線程與多個線程間的協同問題。

CountDownLatch內部維持的int state屬性值,作爲一個計數器,當計數器的值爲0的時,說明所有任務都執行完畢,主任務可以繼續向下執行。state可以理解爲小任務的個數,甚至可以理解爲線程的個數。

先來一段demo,瞅一下CountDwonLatch的用法:

public class TestCountDownLatch {
    private static final int DEFAULT = 3;
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(6);
        List<Thread> threads = new ArrayList<>();
        for (int i = 0; i < 6; i++) {
            Thread thread = new Thread(() -> {
                try {
                    TimeUnit.SECONDS.sleep(DEFAULT);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println(Thread.currentThread().getName() + "count down. ");
                    //CountDownLatch內部維持的計數器-1
                    countDownLatch.countDown();
                    System.out.println(Thread.currentThread().getName() + " " + countDownLatch.getCount());
                }
            });
            threads.add(thread);
        }
        for (Thread thread : threads) {
            thread.start();
        }
        //主線程阻塞,當內部計數器==0時,main線程將被喚醒
        System.out.println("666");
        countDownLatch.await();
        System.out.println("999");
        //state計數器不會恢復成初始參數,此時不再阻塞線程
        countDownLath.await();
        System.out.println("000);
    }
}

構造函數,count爲初始化內部計數器的值:

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

CountDownLatch對外提供的使用方法:

//支持超時時間的可中斷方法,阻塞主線程
public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}
public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
    return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
//子任務線程中計數器-1
public void countDown() {
    sync.releaseShared(1);
}
//獲取當前計數器的值
public long getCount() {
    return sync.getCount();
}

await阻塞主線程的源碼:

public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
//AQS提供的獲取共享鎖框架
public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
        //支持線程中斷時,響應異常
        if (Thread.interrupted())
            throw new InterruptedException();
        //state!=0時,爲負數;則需要阻塞線程
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
}
//CountDownLatch中實現的判斷條件
protected int tryAcquireShared(int acquires) {
    return (getState() == 0) ? 1 : -1;
}
//AQS提供的熟悉的鎖框架
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;
                    }
                }
                //判斷是否可以安全的阻塞,且park後檢測中斷狀態
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            //當被中斷後,需要將node節點置爲null,並從雙向鏈表中刪除。如果前繼是head,還需要喚醒後繼節點,讓其嘗試獲取鎖
            if (failed)
                cancelAcquire(node);
        }
    }
public void countDown() {
   sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
        //根據state是否爲0,來決定是否喚醒鏈表中的阻塞線程
        if (tryReleaseShared(arg)) {
            //
            doReleaseShared();
            return true;
        }
        return false;
    }
//state==0時,直接返回false
protected boolean tryReleaseShared(int releases) {
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }

private void doReleaseShared() {
          for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    //喚醒head後繼節點
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }

CountDownLatch基本就結束了,相對於互斥鎖、讀寫鎖,CountDownLatch就是基於AQS的一個多線程間協同工具。

CountDownLatch的特點:

1、應用於多線程協同場景中,且是單線程等待多線程任務結束

2、CountDownLatch中的state值減爲0後,不會重新恢復成初始參數,因此,countDownLatch.await()被喚醒後,再次調用await()起不到阻塞線程的作用

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章