一、CountDownLatch 源码分析
CountDownLatch 内部定义了静态内部类Sync,继承了AbstractQueuedSynchronizer(下称AQS,可以查看另一篇博客 AQS源码解读),通过重写tryAcquireShared(获取共享锁),tryReleaseShared(释放共享锁)实现了简单的共享锁。只有当前state为0时,获取锁成功。初始化state为n,则需要调用n次countDown()方法,线程才会获取锁成功。获取成功后状态不可逆,CountDownLatch只有 await()获取锁,和countDown()使state减1。共享线程数量通过构造器传入,获取失败则进入AQS内置的FIFO队列。
内部的Sync:
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
// 共享锁 返回 大于0 表示获取到了锁 否则为获取锁失败
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
// 自旋 + CAS将当前state减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;
}
}
}
CountDownLatch 主要有两个方法 await(),countDown()。调用await方法将会一直阻塞至state ==0;countDown() 将state减1。
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public void countDown() {
sync.releaseShared(1);
}
二、CountDownLatch实战
class Driver {
void main() throws InterruptedException {
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(N);
for (int i = 0; i < N; ++i)
new Thread(new Worker(startSignal, doneSignal)).start();
doSomethingElse();
// startSignal.countDown() 后子线程开始执行
startSignal.countDown();
doSomethingElse();
// 阻塞至子线程全部执行完毕
doneSignal.await();
}
}
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) {}
}
void doWork() { ... }
这个是伪代码,并不能直接运行,代码出自CountDownLatch 的类注释上上面。 定义了两个线程信号 startSignal ,doneSignal。当主线程调用startSignal.countDown() 子线程开始执行, doneSignal.await()会阻塞主线程至子线程全部执行完毕调用doneSignal#countDown(),然后继续执行。