CountDownLatch是一个同步工具类,用来多线程协作的帮助工具类。
CountDownLatch依赖一计数器实现同步,可以使线程完成任务后进入阻塞状态,每个线程执行完任务后递减计数器,计数器归零后,阻碍的线程被唤醒继续执行任务。
演示示例:
package com.securitit.serialize.juc;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchTester {
// CountDownLatch实例.
private static final CountDownLatch countDownLatch = new CountDownLatch(3);
public static void main(String[] args) {
// 模拟阻塞线程.
new Thread(() -> {
try {
System.out.println("线程阻塞:" + Thread.currentThread().getName());
countDownLatch.await();
System.out.println("线程唤醒:" + Thread.currentThread().getName());
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}).start();
// 模拟计数器递减线程.
new Thread(() -> {
try {
countDownLatch.countDown();
System.out.println("计数器递减:" + Thread.currentThread().getName());
} catch (Exception ex) {
ex.printStackTrace();
}
}).start();
// 模拟计数器递减线程.
new Thread(() -> {
try {
countDownLatch.countDown();
System.out.println("计数器递减:" + Thread.currentThread().getName());
} catch (Exception ex) {
ex.printStackTrace();
}
}).start();
// 模拟计数器递减线程.
new Thread(() -> {
try {
countDownLatch.countDown();
System.out.println("计数器递减:" + Thread.currentThread().getName());
} catch (Exception ex) {
ex.printStackTrace();
}
}).start();
}
}
输出结果:
线程阻塞:Thread-0
计数器递减:Thread-1
计数器递减:Thread-2
计数器递减:Thread-3
线程唤醒:Thread-0
从输出结果可以看出,线程Thread-0使用countDownLatch.await()后,线程Thread-1、Thread-2、Thread-3分别调用countDownLatch.countDown()进行计数器递减操作,计数器归零后,线程Thread-0恢复运行。
源码分析:
CountDownLatch是依赖AQS完成的线程阻塞和唤醒,利用AQS的共享锁完成锁的多线程获取和释放。以下对CoundDownLatch源码进行解析,关于其中AQS的解析请参照本博博文《AQS - AbstractQueuedSynchronizer 深入源码解析》。
package java.util.concurrent;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
public class CountDownLatch {
// 内部类Sync完成同步器的实现,Sync继承自AQS实现.
private static final class Sync extends AbstractQueuedSynchronizer {
// 序列化版本号.
private static final long serialVersionUID = 4982264981922014374L;
// Constructor.
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;
}
}
}
// 内部类Sync实例.
private final Sync sync;
// Constructor.指定计数器总数.
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
// 阻塞线程,调用AQS的可中断获取共享锁.
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
// 指定超时时间阻塞线程.
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
// 递减计数器.
public void countDown() {
// 逐个释放共享锁.
sync.releaseShared(1);
}
// 获取当前共享锁数量.
public long getCount() {
return sync.getCount();
}
// 重写toString,打印共享锁数量.
public String toString() {
return super.toString() + "[Count = " + sync.getCount() + "]";
}
}
从源码看出,CountDownLatch的实现比较简单,主要都是调用AQS关于共享锁的实现来完成的,初始化时,获取指定计数总数,每个线程每次调用countDown()都会将计数器减一。尽管CountDownLatch看起来完美,但还是存在一些不足,比如:CountDownLatch是一次性的,初始化后,进行业务操作,计数器归零,则这个实例使命完成,无法服务这个实例。
注:文中源码均来自于JDK1.8版本,不同版本间可能存在差异。
如果有哪里有不明白或不清楚的内容,欢迎留言哦!