CountDownLatch 的作用是允许一个或多个线程进入阻塞,直到 countDown
把 state 的值减到 0 才去唤醒线程。
CountDownLatch 本质上使用共享锁,这点体现在释放锁时使用了 AQS#releaseShared
方法。这使得 CountDownLatch 在释放时,能够把同步队列中的线程逐一全部唤醒。
CountDownLatch 有一个静态内部类 Sync 继承自 AbstractQueuedSynchronizer。
public class CountDownLatch {
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
//构造方法中把 state 设置为指定的值
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
//调用链:
//CountDownLatch#await/await(time) -> AQSacquireSharedInterruptibly/tryAcquireSharedNanos -> tryAcquireShared(本方法)
//只要 state != 0,都返回 -1,表示阻塞线程,详情请看 JAVA-Lock解析-三。
//只有当 state == 0 时,才会返回 1,表示线程退出等待并可以唤醒下一个 Node。
//这也证明了当执行 await 时如果 state == 0,为什么会直接返回,不会阻塞。
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
//调用链:
// CountDownLatch#countDown ->AQS#releaseShared -> tryReleaseShared(本方法)
//本质上就是把 state - 1,但只有减到 0 才会返回 true,进入唤醒模式。否则不做任何操作。
//详情请看 JAVA-Lock解析-三。
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;
}
}
}
private final Sync sync;
//count:表示在唤醒阻塞线程前需要调用 countDown 方法的次数
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
//只要不被中断,线程会阻塞直至 state 减至 0。
//如果 state 已经是 0,则立刻返回,不会阻塞。
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
//线程会阻塞直至 state 减至 0,除非被中断或超时
//如果 state 已经是 0,则立刻返回,不会阻塞。
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
//使 state -1,如过 state = 0,则开始唤醒队列中阻塞的线程
public void countDown() {
sync.releaseShared(1);
}
//获取当前的 state 值
public long getCount() {
return sync.getCount();
}
public String toString() {
return super.toString() + "[Count = " + sync.getCount() + "]";
}
}
总结
可以看到 CountDownLatch 还是非常简单的,但前提是你对 AQS 非常熟悉。另外官方文章还提到了 CountDownLatch 一种特殊情况的替代 CyclicBarrier
(内部使用的 ReentrantLock),有兴趣的可以自己看下。希望对大家有帮助,如有错误请指正。
网友评论