描述
CountDownLatch中count down是倒数的意思,latch则是门闩的含义;在构造CountDownLatch的时候需要传入一个整数n,在这个整数“倒数”到0之前,主线程需要等待在门口,而这个“倒数”过程则是由各个执行线程驱动的,每个线程执行完一个任务“倒数”一次。总结来说,CountDownLatch的作用就是等待其他的线程都执行完任务,必要时可以对各个任务的执行结果进行汇总,然后主线程才继续往下执行。
CountDownLatch是基于AbstractQueuedSynchronizer实现的,实现了AQS的共享锁部分。创建一个CountDownLatch对象时,所传入的整数n就会赋值给AQS的state属性(锁数量)。
使用示例
class Driver {
// ...
void main() throws InterruptedException {
//控制所有线程一起执行.
CountDownLatch startSignal = new CountDownLatch(1);
//N表示启动的线程数量.
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() { ... }
}
CountDownLatch结构
CountDownLatch实现AQS的共享锁,在初始化实例时传入count就是锁的数量,
可以看到await时,处理AQS.tryAcquireShared传入的锁数量值并没有使用,只是简单的获取一个共享锁资源。执行countDown时通过AQS.releaseShared释放一个锁资源.
public class CountDownLatch {
//AQS同步队列的实现
private static final class Sync extends AbstractQueuedSynchronizer {
Sync(int count) {
setState(count);
}
...............
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
protected boolean tryReleaseShared(int releases) {...}
}
private final Sync sync;
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
..........
public void countDown() {
sync.releaseShared(1);
}
..............
}
tryReleaseShared释放锁
在CountDownLatch中释放锁时,只是对锁的state进行减1操作,每个countDown调用时释放一次。直接所有的线程都释放完,state值规0或线程被中断。
//CountDownLatch释放锁资源
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
CAS自旋,这里传入的releases要求释放的个数并没有使用,只是对state减1.
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
await等待结果
CountDownLatch的await不同处在于,在Sync对AQS.tryAcquireShared实现中,只有满足state值为0时才停止迭代,否则一直递归判断stage的值,而stage的值在每个线程调用countDown函数是都会对state值减1.
//CountDownLatch await等待执行结果
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
//AQS.acquireSharedInterruptibly
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
//线程被中断直接抛出异常,线程应该去处理.
if (Thread.interrupted())
throw new InterruptedException();
//如果Sync实现中的tryAcquireShared返回值小于0,说明还需要等。
//doAcquireSharedInterruptibly递归调用tryAcquireShared,不断判断是否还需要等待.
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
Sync.tryAcquireShared实现,只有当CountDownLatch的state值被释放到0时,返回值才大于0.
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}