# CountDownLatch类解析

# 是什么

CountDownLatch是一种多线程同步的辅助工具。假设你有一组操作,和一个最终操作,最终操作需要等得前面的操作结束才执行,这个时候就可以用它。

CountDownLatch可以认为是一种倒数机制,在设置一个size后,调用其await方法的线程会等待到size为0后才会释放。

注意CountDownLatch是一个一次性的计数器,没有接口重置内部的计数,只能对计数做减法。

# 怎么用

CountDownLatch的size设置为1时,可以理解为一种门闩机制,或者阀门。当阀门关闭时,所有调用await的线程陷入等待状态(被挡起来)直到阀门打开。 当size设为N时,可以认为是所有调用await的线程在一连串N个操作,或者某些操作执行N次后才去执行。

下文代码照搬JDK注释示例。

假设一个场景是,我们初始化一个Driver,然后其他线程等待Driver执行完毕后,再分别执行对应的操作。Driver等待其他线程全部执行完毕后返回。那么就可以用下文的方式: startSignal负责作为Driver的门闩,在Driver执行完毕后打开其他线程的执行门闩,让其他线程的内部逻辑运行。与此同时Driver用doneSignal等待其他线程全部执行完成。

class Driver { // ...
   void main() throws InterruptedException {
     CountDownLatch startSignal = new CountDownLatch(1);
     CountDownLatch doneSignal = new CountDownLatch(N);

     for (int i = 0; i < N; ++i) // create and start threads
       new Thread(new Worker(startSignal, doneSignal)).start();

     doSomethingElse();            // don't let run yet
     startSignal.countDown();      // let all threads proceed
     doSomethingElse();
     doneSignal.await();           // wait for all to finish
   }
 }

 class Worker implements Runnable {
   private final CountDownLatch startSignal;
   private final CountDownLatch doneSignal;
   Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
     this.startSignal = startSignal;
     this.doneSignal = doneSignal;
   }
   public void run() {
     try {
       startSignal.await();
       doWork();
       doneSignal.countDown();
     } catch (InterruptedException ex) {} // return;
   }

   void doWork() { ... }
 }

或者我们将某一个“操作”划分成N部分,这N部分可以并行执行,且主线程等待N部分执行完毕后返回。我们就可以在主线程使用doneSignal做N部分执行的判断。

class Driver2 { // ...
   void main() throws InterruptedException {
     CountDownLatch doneSignal = new CountDownLatch(N);
     Executor e = ...

     for (int i = 0; i < N; ++i) // create and start threads
       e.execute(new WorkerRunnable(doneSignal, i));

     doneSignal.await();           // wait for all to finish
   }
 }

 class WorkerRunnable implements Runnable {
   private final CountDownLatch doneSignal;
   private final int i;
   WorkerRunnable(CountDownLatch doneSignal, int i) {
     this.doneSignal = doneSignal;
     this.i = i;
   }
   public void run() {
     try {
       doWork(i);
       doneSignal.countDown();
     } catch (InterruptedException ex) {} // return;
   }

   void doWork() { ... }
 }

# 内部原理

CountDownLatch本身代码比较简单,其内部定义了内部类Sync,继承自AbstractQueuedSynchronizer,也就是AQS。

1

其覆盖了tryAcquireShared和tryReleasedShared方法,使得每次release时将state状态-1, acquire时忽略参数,直接判断state是否为0。所以可见其并没有使用AQS更灵活的状态机制,而是将其简化成计数,而使用了其完善的并发同步机制。

其await方法和countDown方法均使用AQS的shared mode接口来进行。await相当于acquire 1, countDown相当于release 1

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

public boolean await(long timeout, TimeUnit unit)
    throws InterruptedException {
    return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}

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

public long getCount() {
    return sync.getCount();
}