CountDownLatch也叫做闭锁,是一种倒计时计数器。CountDownLatch等待多个线程都执行完成后,再继续做后面的事情。最常见的一个应用场景就是开启多个线程同时执行某个任务,等到所有任务都执行完在统计结果。CountDownLatch内部提供了一个计数器,在构造CountDownLatch时必须执行计数器的初始值,且计数器的初始值必须大于0。另外提供countDown()方法来操作计数器的值,每调用一次CountDown()方法计数器就会减1,直到计数器的值减到0时就代表条件成熟,所有因调用await()方法而阻塞的线程都会被唤醒。 下面我们通过源码来具体分析。
Sync内部类源码分析
//Sync继承 AQS使用起state属性来作为count
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
//构造器
Sync(int var1) {
//设置state的值为count
this.setState(var1);
}
//获取count的值,及state的值
int getCount() {
return this.getState();
}
//重写父类tryAcquireShared的方法
protected int tryAcquireShared(int var1) {、
//如果state值为0;返回1否则返回-1
return this.getState() == 0 ? 1 : -1;
}
//重写 父类tryReleaseShared方法
protected boolean tryReleaseShared(int var1) {
for (;;) {
//获取state的值
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
//cas更新成功
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
CountDownLatch 构造函数
public CountDownLatch(int count) {
//构造函数传入的值必须大于0;否则抛出异常
if (count < 0) throw new IllegalArgumentException("count < 0");
//创建一个Sync对象
this.sync = new Sync(count);
}
await() 方法解析
当前线程调用了await()方法后会,会将当前线程阻塞直到出现下面两种情况之一才会返回:
当所有线程调用都调用了countDown方法后,也就是说调用了await方法的都要在调用countDown方法一遍使计数器的值为0
其他线程调用了当前线程的interrupt方法中断了当前线程, 当前线程会抛出interruptedException异常返回。
public void await() throws InterruptedException {
//sync调用父类的acquireSharedInterruptibly方法
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
//如果线程被中断,则抛出异常
if (Thread.interrupted())
throw new InterruptedException();
//如果tryAcquireShared小于0,则进入AQS同步队列
if (tryAcquireShared(arg) < 0)
//调用AQS的方法进入同步队列
doAcquireSharedInterruptibly(arg);
}
Sync类的tryAcquireShared方法在state等于0时返回1;否则返回-1
回到AQS的AcquireSharedInterruptibly方法,当Sync类的tryAcquireShared返回1则回到AQS的AcquireSharedInterruptibly方法返回,即await方法返回;
await(long timeout,TimeUnit unit)源码分析
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
AQS中的tryAcquireSharedNanos方法
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquireShared(arg) >= 0 ||
doAcquireSharedNanos(arg, nanosTimeout);
}
AQS中的doAcquireSharedNanos方法
private boolean doAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (nanosTimeout <= 0L)
return false;
final long deadline = System.nanoTime() + nanosTimeout;
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return true;
}
}
nanosTimeout = deadline - System.nanoTime();
if (nanosTimeout <= 0L)
return false;
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
countDown()方法
当前线程调用了该方法后会递减计数器的值,递减后如果计数器为0,则会唤醒await方法而被阻塞的线程,否则什么都不做。
public void countDown() {
sync.releaseShared(1);
}
AQS中的releaseShared方法
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
AQS中的doReleaseShared 方法:
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}