一、前言
前面已经通过小例子介绍了CountDownLatch的基本使用场景,在本篇博客中我们简要的看下CountDownLatch的源码,看看是如何实现的,前提是必须先要知道AQS的大体流程,因为CountDownLatch是基于AQS实现的,不清楚的可以先看《AbstractQueuedSynchronizer源码——基本认识》。
二、源码
整体上CountDownLatch的源码很少,一个内部类外加几个方法,下面我们就以之前用到的几个方法来看看。
public class CountDownLatch {
private static final class Sync extends AbstractQueuedSynchronizer {...}
private final Sync sync;
public CountDownLatch(int count) {...}
public void await() throws InterruptedException {...}
public boolean await(long timeout, TimeUnit unit) throws InterruptedException {...}
public void countDown() {...}
public long getCount() {...}
public String toString() {...}
}
2.1 构造方法
构造方法里面我们可以看到,传入一个参数创建了一个Sync对象,那个这个Sync就是CountDownLatch的内部类。
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
2.2 Sync类
Sync继承了AQS,并且重写了AQS中的tryAcquireShared、tryReleaseShared方法,这个之前介绍AQS时就说过,这两个是AQS子类自行设计逻辑。
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -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构造方法中调用了Sync的构造方法,Sync中又调用了AQS的setState方法,这里可以看到最初创建CountDownLatch对象时传入的参数是赋值给了AQS中的state。
// #AbstractQueuedSynchronizer
protected final void setState(int newState) {
state = newState;
}
2.3 await
在之前的例子中,我们在需要等待的地方调用了await方法,await中调用了AQS中的acquireSharedInterruptibly,此方法会相应中断。
public void await() throws InterruptedException {
// acquireSharedInterruptibly跟acquireShared逻辑基本一致,区别就是acquireSharedInterruptibly是响应中断的
sync.acquireSharedInterruptibly(1);
}
在acquireSharedInterruptibly中会调用tryAcquireShared尝试获取资源,那么这个tryAcquireShared方法就是上面提到过的,在CountDownLatch中的内部类Sync中重写的。
// #AbstractQueuedSynchronizer
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
可以看到,如果当前的state=0,那就会返回1,否则返回-1。
// #CountDownLatch.Sync
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
所以,综合上面三个方法的调用关系,如果执行await时计数不为0,那么就会在acquireSharedInterruptibly方法中执行doAcquireSharedInterruptibly方法,当前线程就会进入等待队列,并且转为阻塞状态等待唤醒(具体过程在《AbstractQueuedSynchronizer源码——基本认识》有说明),这就是例子中某个线程调用了await后会出现等待的原因。
2.4 countDown
在例子中,子线程完成逻辑后都会调用countDown,countDown方法会让当前技术减1。
public void countDown() {
// AbstractQueuedSynchronizer的方法
sync.releaseShared(1);
}
//# AbstractQueuedSynchronizer
public final boolean releaseShared(int arg) {
// 这里的tryReleaseShared也是在Sync中重写了
if (tryReleaseShared(arg)) {
// 唤醒下一个线程或者设置传播状态
doReleaseShared();
return true;
}
return false;
}
// #CountDownLatch.Sync
protected boolean tryReleaseShared(int releases) {
// 自旋,减1
for (;;) {
int c = getState();
// state等于0,结束返回false
if (c == 0)
return false;
// state不等于0时,state减1
int nextc = c-1;
// 并通过CAS操作确保state设置成功,否则再次循环知道设置成功,然后跳出循环返回判断结果
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
通过tryReleaseShared方法的返回值,然后根据判断是否在releaseShared中调用doReleaseShared方法,该方法就是用来唤醒下一个线程或者设置传播状态的(具体过程在《AbstractQueuedSynchronizer源码——基本认识》有说明),所以可以看到在例子中当某个线程执行countDown方法后,若计数减1刚好等于0,那么就会触发执行唤醒线程的方法,从而使得此前调用await方法而进入阻塞状态的线程被唤醒继续执行。
三、小结
上面就是关于CountDownLatch的基本介绍,总体而言逻辑并不复杂,但是前提是必选先弄清楚AQS的大体流程。