簡介:

CountDownLatch 是一個非常實用的多執行緒控制工具類,通常用來控制執行緒的等待,它可以讓某個執行緒等待直到倒計時結束

CountDownLatch 提供了兩個主要的方法,await()、countDown()。

  • await:使當前執行緒阻塞,等待計數器為 0

  • countDown:計數器減一,計數為零時,釋放所有在等待的執行緒

例項:

public class CountDownLatchDemo implements Runnable {
static final CountDownLatch end = new CountDownLatch(10);
static final CountDownLatchDemo demo = new CountDownLatchDemo();
@Override
public void run() {
try {
Thread.sleep(new Random().nextInt(10) * 1000);
System.out.println("check complete...");
end.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
ExecutorService exec = Executors.newFixedThreadPool(10);
for (int i = 0;i < 10;i++) {
exec.submit(demo);
}
end.await();
System.out.println("Fire!");
exec.shutdown();
}
}

原理解析:

countDownLatch 的計數是通過一個共享變數(volatile)實現的,下面分析它的三個核心函式:建構函式,CountDownLatch(int count);阻塞執行緒,await();計數器減一,countDown()。

CountDownLatch(int count)

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

其中 Sync 是 CountDownLatch 的內部類,並且 Sync 繼承了 AbstractQueuedSynchronizer

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

其中 setState 是設定 AbstractQueuedSynchronizer 中 state 變數,該變數聲明瞭 volatile。

State 就是 countDownLatch 中的計數器。

await()

public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquireShared(arg) >= 0 ||
doAcquireSharedNanos(arg, nanosTimeout);
}

acquireSharedInterruptibly() 的作用是獲取共享鎖,如果當前執行緒處於中斷狀態,則丟擲 InterruptedException,否則,呼叫 tryAcquireShared(arg) 嘗試獲取共享鎖,如果鎖計數器 = 0,則表示鎖為可獲取狀態,返回 1,否則,鎖為不可獲取狀態,則返回 -1。

doAcquireSharedNanos() 會使當前執行緒一直等待,直到當前執行緒獲取到共享鎖(或執行緒被中斷)才返回。

countDown()

public void countDown() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}

releaseShared() 目的是讓當前執行緒釋放它所持有的共享鎖。

tryReleaseShared() 的作用是釋放共享鎖,將鎖計數器的值減一。

總結

CountDownLatch 是通過共享鎖實現的。CountDownLatch 建構函式傳遞 int 引數,該引數是計數器的初始狀態,表示共享鎖最多能被 count 個執行緒同時獲取。

當某執行緒呼叫 CountDownLatch 的 await 方法時,該執行緒會等待共享鎖可用時(計數器為 0 時),才能獲取共享鎖,進而繼續執行。

每次執行 countDown 時,會將計數器減一。

參考資料

Java多執行緒系列--“JUC鎖”09之 CountDownLatch原理和示例