1. 概述
CountDownLatch 可以翻译为倒计时器,它是一个线程或多个线程等待其它线程执行完操作后再执行后续操作。
最常见的场景就是异步调用多个接口,等多个接口执行完成返回结果后继续后续操作。
2. 示例
public class CountDownLatchTest {
public static void main(String[] args) {
final CountDownLatch latch = new CountDownLatch(2);
System.out.println("主线程开始执行");
//第一个子线程执行
new Thread(() -> {
try {
Thread.sleep(3000);
System.out.println("子线程:"+Thread.currentThread().getName()+"执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown();
}, "第一个子线程").start();
//第二个子线程执行
new Thread(() -> {
try {
Thread.sleep(3000);
System.out.println("子线程:"+Thread.currentThread().getName()+"执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown();
}, "第二个子线程").start();
System.out.println("等待两个线程执行完毕");
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("两个子线程都执行完毕,继续执行主线程");
}
}
输出结果
主线程开始执行
等待两个线程执行完毕
子线程:第二个子线程执行
子线程:第一个子线程执行
两个子线程都执行完毕,继续执行主线程
3. 源码
- CountDownLatch 类中只提供了一个构造器
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
// count赋值给state做计数使用
this.sync = new Sync(count);
}
- 类中有三个方法是最重要的
// 调用await()方法的线程会被加入AQS队列挂起,它会等待直到count值为0才继续执行
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
// 和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
// 将count值减1
public void countDown() {
sync.releaseShared(1);
}
具体细节源码就不展开分析了,和之前的ReentrantLock、Semaphore的基本结构大体相似。
参考:ReentrantLock、 Semaphore
4. 总结
- CountDownLatch 表示允许一个或多个线程等待其它线程的操作执行完毕后再执行后续的操作
- CountDownLatch 使用 AQS 的共享锁机制实现
- CountDownLatch 初始化的时候需要传入次数count
- 每次调用 countDown() 方法 count 的次数减1
- 每次调用 await() 方法的时候会尝试获取锁,这里的获取锁其实是检查 AQS 的 state 变量的值是否为0
- 当 count 的值(也就是 state 的值)减为0的时候会唤醒排队着的线程(这些线程调用 await() 进入队列)
- CountDownLatch 的通常用法和 Thread.join() 有点类似。Thread.join() 是在主线程中调用的,它只能等待被调用的线程结束了才会通知主线程,而 CountDownLatch 则不同,它的 countDown() 方法可以在线程执行的任意时刻调用,灵活性更大