在多线程编程中,协调多个线程的执行顺序是一个常见且重要的问题。Java 提供了多种同步工具类来帮助开发者实现线程之间的协作与控制,其中 `CountDownLatch` 就是其中之一。它在实际开发中被广泛用于控制线程的启动、等待或结束等场景。
一、什么是 CountDownLatch?
`CountDownLatch` 是 Java 并发包(`java.util.concurrent`)中的一个同步工具类,它的核心功能是允许一个或多个线程等待其他线程完成操作后再继续执行。它通过一个计数器来实现这一机制,初始时设置一个数字,每当一个线程完成任务,该数字就减一,当计数器归零时,所有等待的线程将被唤醒并继续执行。
二、基本用法
1. 初始化
创建一个 `CountDownLatch` 实例时需要传入一个初始值:
```java
CountDownLatch latch = new CountDownLatch(3);
```
这里的 `3` 表示有三个线程需要完成任务,只有当这三个线程都完成后,等待的线程才能继续运行。
2. 等待
调用 `await()` 方法可以让当前线程进入等待状态,直到计数器变为零:
```java
latch.await();
```
也可以设置超时时间,避免无限等待:
```java
latch.await(10, TimeUnit.SECONDS);
```
3. 计数减一
当某个线程完成任务后,调用 `countDown()` 方法将计数器减一:
```java
latch.countDown();
```
三、典型应用场景
1. 主线程等待子线程完成
比如,在启动多个子线程进行数据处理后,主线程需要等待所有子线程执行完毕再进行后续操作:
```java
public class Example {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
final int index = i;
new Thread(() -> {
System.out.println("Thread " + index + " is running.");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread " + index + " finished.");
latch.countDown();
}).start();
}
latch.await();
System.out.println("All threads have completed.");
}
}
```
2. 多线程协同工作
在一些需要多个线程协同工作的场景中,如数据库连接池初始化、资源加载等,`CountDownLatch` 可以确保所有资源准备就绪后再进行下一步操作。
四、注意事项
- `CountDownLatch` 是一次性使用的,一旦计数器减到零,就不能再次使用。如果需要重复使用,可以考虑使用 `CyclicBarrier`。
- `countDown()` 方法可以被多次调用,但不能超过初始值,否则会抛出异常。
- 它不支持中断操作,如果线程在 `await()` 中等待,无法通过中断来唤醒。
五、总结
`CountDownLatch` 是 Java 并发编程中非常实用的工具类,它可以帮助我们更灵活地控制线程之间的执行顺序和依赖关系。通过合理使用这个类,我们可以提高程序的并发性能和可维护性。对于需要等待多个任务完成的场景来说,它是一个非常有力的辅助工具。