CountDownLatch使用
CountDownLatch使用[编辑 | 编辑源代码]
介绍[编辑 | 编辑源代码]
CountDownLatch 是 Java 并发编程中的一个同步辅助类,允许一个或多个线程等待其他线程完成操作后再继续执行。它通过一个计数器实现,计数器的初始值由构造函数指定,线程调用 `countDown()` 方法减少计数器,而调用 `await()` 方法的线程会阻塞,直到计数器减至零。
CountDownLatch 适用于以下场景:
- 主线程等待多个子线程完成初始化任务后再继续执行。
- 多个线程并行执行任务,主线程等待所有任务完成后再汇总结果。
- 模拟并发测试,确保所有线程同时启动。
核心方法[编辑 | 编辑源代码]
CountDownLatch 的核心方法包括:
- `CountDownLatch(int count)`:构造函数,指定初始计数值。
- `void await()`:阻塞当前线程,直到计数器减至零。
- `boolean await(long timeout, TimeUnit unit)`:带超时的 `await()`,在指定时间内等待计数器归零。
- `void countDown()`:计数器减一,如果计数器为零,则释放所有等待线程。
- `long getCount()`:返回当前计数器的值。
代码示例[编辑 | 编辑源代码]
以下是一个简单的示例,展示如何使用 CountDownLatch 等待多个线程完成任务:
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
final int THREAD_COUNT = 3;
CountDownLatch latch = new CountDownLatch(THREAD_COUNT);
for (int i = 0; i < THREAD_COUNT; i++) {
new Thread(() -> {
System.out.println("线程 " + Thread.currentThread().getName() + " 开始执行任务");
try {
Thread.sleep(1000); // 模拟任务执行时间
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程 " + Thread.currentThread().getName() + " 完成任务");
latch.countDown(); // 计数器减一
}).start();
}
System.out.println("主线程等待所有子线程完成任务...");
latch.await(); // 阻塞直到计数器归零
System.out.println("所有子线程完成任务,主线程继续执行");
}
}
输出示例:
线程 Thread-0 开始执行任务 线程 Thread-1 开始执行任务 线程 Thread-2 开始执行任务 主线程等待所有子线程完成任务... 线程 Thread-0 完成任务 线程 Thread-1 完成任务 线程 Thread-2 完成任务 所有子线程完成任务,主线程继续执行
解释: 1. 主线程创建 `CountDownLatch`,初始值为 3(对应 3 个子线程)。 2. 每个子线程完成任务后调用 `countDown()`,计数器减一。 3. 主线程调用 `await()` 阻塞,直到计数器归零,然后继续执行。
实际应用场景[编辑 | 编辑源代码]
场景 1:多线程初始化[编辑 | 编辑源代码]
在系统启动时,可能需要多个服务(如数据库连接、缓存加载)并行初始化,主线程等待所有服务就绪后再启动应用。
public class ServiceInitializer {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(2);
new Thread(() -> {
System.out.println("初始化数据库连接...");
// 模拟初始化耗时
try { Thread.sleep(2000); } catch (InterruptedException e) {}
System.out.println("数据库连接就绪");
latch.countDown();
}).start();
new Thread(() -> {
System.out.println("加载缓存数据...");
// 模拟加载耗时
try { Thread.sleep(1500); } catch (InterruptedException e) {}
System.out.println("缓存数据加载完成");
latch.countDown();
}).start();
latch.await();
System.out.println("所有服务初始化完成,启动应用");
}
}
场景 2:并发测试[编辑 | 编辑源代码]
在性能测试中,确保所有线程同时开始执行任务:
public class ConcurrentTest {
public static void main(String[] args) throws InterruptedException {
final int THREAD_COUNT = 5;
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(THREAD_COUNT);
for (int i = 0; i < THREAD_COUNT; i++) {
new Thread(() -> {
try {
startSignal.await(); // 等待开始信号
System.out.println("线程 " + Thread.currentThread().getName() + " 执行任务");
doneSignal.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
System.out.println("准备开始并发测试...");
Thread.sleep(1000); // 模拟准备时间
startSignal.countDown(); // 释放所有线程
doneSignal.await(); // 等待所有线程完成
System.out.println("测试结束");
}
}
与 CyclicBarrier 的区别[编辑 | 编辑源代码]
CountDownLatch 和 `CyclicBarrier` 都用于线程同步,但有以下区别:
- 计数器重置:CountDownLatch 的计数器不可重置,而 CyclicBarrier 可以重复使用。
- 线程角色:CountDownLatch 的线程分为等待线程和计数线程,而 CyclicBarrier 的线程互相等待。
- 用途:CountDownLatch 用于一个线程等待多个线程,CyclicBarrier 用于多个线程互相等待。
注意事项[编辑 | 编辑源代码]
1. 计数器必须为正整数,否则抛出 `IllegalArgumentException`。 2. `await()` 方法可能被中断,需处理 `InterruptedException`。 3. 避免过度使用 CountDownLatch,在复杂场景中考虑 `CompletableFuture` 或 `Phaser`。
总结[编辑 | 编辑源代码]
CountDownLatch 是 Java 并发编程中强大的同步工具,适用于主线程等待多个子线程完成的场景。通过计数器机制,它简化了线程间的协调工作。理解其原理和适用场景,能有效提升多线程程序的可靠性和性能。