# 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。
其覆盖了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();
}