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,初始计数为 3CountDownLatch 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("主线程等待所有任务完成...");// 主线程等待直到计数器为 0latch.await();System.out.println("所有任务完成,主线程继续执行。");}
}
在这个例子中,主线程调用 latch.await()
后会阻塞,直到 3 个工作线程分别调用 countDown()
,使计数器归零。计数器归零后,主线程被唤醒,继续执行后续代码。
5. 注意事项
-
一次性使用
CountDownLatch
的计数器只能使用一次,归零后不能重新设置。 -
正确调用
必须保证每个任务都能调用countDown()
,否则等待的线程可能永远无法被唤醒。