1. 基本原理
-
计数器
CountDownLatch
在创建时需要指定一个初始计数值。这个值通常代表需要等待完成的任务数或线程数。 -
等待与递减
- 等待:调用
await()
方法的线程会被阻塞,直到计数器变为 0。 - 递减:每当一个任务完成后,应调用
countDown()
方法来将计数器减 1。当计数器减到 0 时,所有等待的线程将被唤醒,继续执行后续任务。
- 等待:调用
2. 主要方法
-
await()
当前线程调用此方法后,会等待直到计数器归零。也可以调用带超时参数的版本来等待一定时间后放弃等待。 -
countDown()
每当一个任务完成时,调用此方法,将计数器减 1。一旦计数器为 0,所有等待的线程都会被释放。
3. 使用场景
-
任务等待
常见于主线程等待多个子线程完成各自的任务后,再进行汇总或后续处理。例如,启动多个任务加载数据,主线程在所有任务完成后开始处理数据。 -
并发启动
可以用来确保多个线程在同一时刻开始工作。例如,所有线程等待一个“发令枪”信号后同时开始执行任务。 -
一次性计数器
一旦计数器归零,CountDownLatch
就不能再重置,因此它适合用于一次性等待场景。
4. 示例代码
下面的代码展示了如何使用 CountDownLatch
等待多个线程完成任务后,再让主线程继续执行:
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
// 创建 CountDownLatch,初始计数为 3
CountDownLatch latch = new CountDownLatch(3);
// 启动 3 个线程,每个线程执行任务后调用 countDown()
for (int i = 0; i < 3; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " 正在执行任务...");
Thread.sleep(2000); // 模拟任务执行耗时
System.out.println(Thread.currentThread().getName() + " 任务完成。");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
latch.countDown(); // 任务完成后,计数器减 1
}
}, "线程" + (i + 1)).start();
}
System.out.println("主线程等待所有任务完成...");
// 主线程等待直到计数器为 0
latch.await();
System.out.println("所有任务完成,主线程继续执行。");
}
}
在这个例子中,主线程调用 latch.await()
后会阻塞,直到 3 个工作线程分别调用 countDown()
,使计数器归零。计数器归零后,主线程被唤醒,继续执行后续代码。
5. 注意事项
-
一次性使用
CountDownLatch
的计数器只能使用一次,归零后不能重新设置。 -
正确调用
必须保证每个任务都能调用countDown()
,否则等待的线程可能永远无法被唤醒。