CountDownLoatch是JUC下一個用於控制計數的計數器,比如我需要從6開始計數,每個線成執行完之後計數減一,等計數器到0時候開始執行其他任務。

public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; i < 6; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t結束");
countDownLatch.countDown();
}, String.valueOf(i)).start();
}
countDownLatch.await();
System.out.println("開始執行新任務");
}

現在讓我們來看下CountDownLoach的底層原始碼

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;
}
}
} private final Sync sync;

這裡最重要的是這個tyrReleaseShared方法,CountDownLatch也是實現了AQS機制,通過state去判斷資源知否被佔用。當我們使用countDow函式去減1時,會觸發releaseShared方法,這個方法的引數時每次減的次數,預設是1

public void countDown() {
sync.releaseShared(1);
}

我們點進去看看這個方法是如何實現的

 public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}

這個方法呼叫了AQS的releaseShared,他會先去嘗試減一,判斷是否減一成功

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;
}
}

這裡使用了CAS自旋機制對state(我們這裡是6)進行減1,如果自旋成功就會返回true即6一個一個減最後變成0,就會執行下面的doReleaseShared方法

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
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}

這裡使用了LockSupport相關的加鎖解鎖,unpark有關這個我會在之後有空的時候進行解析,通過對AQS任務佇列進行判斷,是否只有頭節點和尾節點,這個任務佇列裡面頭節點並不是真正的任務,而是為了初始化方便操作的哨兵節點,然後對第一個任務節點進行操作,就是我們這6步操作,每次減一作為一個任務進行釋放(我自己認為的)。

這是我第一次發帖子希望大家多多支援,準備秋招了,爭取大廠上岸,如果帖子裡有錯誤希望大家積極提出來