Java 多线程:CountDownLatch 实现线程同步教程
·
Java 多线程:CountDownLatch 实现线程同步教程
在本教程中,我将逐步解释如何使用 Java 中的 CountDownLatch 类实现线程同步。CountDownLatch 是 java.util.concurrent 包中的一个工具,它允许一个或多个线程等待其他线程完成一组操作,从而实现高效同步。教程结构清晰,包括介绍、工作原理、代码示例和解释,确保您能轻松掌握。
1. CountDownLatch 简介
CountDownLatch 是一个同步辅助类,用于协调多个线程的执行。它基于一个计数器工作:
- 初始化时指定一个整数计数器值(例如 $n$)。
- 线程调用
countDown()方法减少计数器值(每次减少 1)。 - 当计数器值达到 0 时,所有等待的线程(通过
await()方法)被唤醒并继续执行。 这适用于场景如:主线程等待多个子线程完成初始化任务,或并行任务完成后触发后续操作。
2. 工作原理
CountDownLatch 的核心方法:
CountDownLatch(int count):构造函数,初始化计数器值 $count$。void countDown():调用后,计数器值减少 1。如果计数器值变为 0,则唤醒所有等待线程。void await():调用线程会阻塞,直到计数器值变为 0。如果计数器值已为 0,则立即返回。
工作流程:
- 主线程创建
CountDownLatch实例,设置初始计数器值(例如 $n=3$)。 - 启动多个子线程,每个线程执行任务后调用
countDown()。 - 主线程调用
await()阻塞,直到所有子线程完成(计数器值变为 0)。 - 当计数器为 0 时,主线程恢复执行。
优点:简单高效,避免了复杂的锁机制,适用于一次性同步事件。
3. 代码示例
下面是一个完整的 Java 代码示例,模拟一个场景:主线程启动 3 个工作线程,每个线程执行一个任务(如下载文件),主线程等待所有任务完成后打印总结信息。
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
// 初始化 CountDownLatch,计数器值设置为 3(表示有 3 个任务)
final CountDownLatch latch = new CountDownLatch(3);
// 创建并启动 3 个工作线程
for (int i = 1; i <= 3; i++) {
new Thread(new Worker(latch, i)).start();
}
System.out.println("主线程等待所有工作线程完成任务...");
// 主线程调用 await() 阻塞,直到计数器值为 0
latch.await();
System.out.println("所有工作线程已完成!主线程继续执行。");
}
// 工作线程类
static class Worker implements Runnable {
private final CountDownLatch latch;
private final int workerId;
public Worker(CountDownLatch latch, int workerId) {
this.latch = latch;
this.workerId = workerId;
}
@Override
public void run() {
try {
System.out.println("工作线程 " + workerId + " 开始执行任务...");
// 模拟任务执行时间(随机延迟)
Thread.sleep((long) (Math.random() * 1000));
System.out.println("工作线程 " + workerId + " 完成任务!");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 任务完成后调用 countDown(),减少计数器值
latch.countDown();
}
}
}
}
4. 代码解释
- 初始化:
CountDownLatch latch = new CountDownLatch(3);创建一个计数器初始值为 3 的CountDownLatch实例。 - 工作线程:每个
Worker线程模拟执行任务(通过Thread.sleep模拟耗时)。在run()方法中:- 线程执行完任务后,在
finally块中调用latch.countDown(),确保计数器减少(即使发生异常)。 - 计数器值减少表示一个任务完成。
- 线程执行完任务后,在
- 主线程:
- 调用
latch.await()阻塞,直到计数器值变为 0。 - 当所有工作线程完成(计数器值达到 0),主线程恢复执行并打印消息。
- 调用
- 同步效果:主线程会等待所有 3 个工作线程完成,确保“所有工作线程已完成”消息只在所有任务结束后打印。
运行此代码,您会看到输出类似:
主线程等待所有工作线程完成任务...
工作线程 1 开始执行任务...
工作线程 2 开始执行任务...
工作线程 3 开始执行任务...
工作线程 1 完成任务!
工作线程 2 完成任务!
工作线程 3 完成任务!
所有工作线程已完成!主线程继续执行。
5. 总结与注意事项
- 优点:
CountDownLatch实现简单,资源消耗低,适用于一次性同步(计数器值不能重置)。 - 常见场景:初始化屏障(如数据库连接池初始化)、并行计算后聚合结果。
- 注意事项:
- 计数器值初始化后不能更改。
- 确保每个任务线程都调用
countDown(),否则await()可能永久阻塞。 - 避免在任务中长时间阻塞,以免影响性能。
- 结合其他同步工具(如
ExecutorService)使用更高效。
通过本教程,您应该能理解并应用 CountDownLatch 实现线程同步。如果有进一步问题,如异常处理或高级用法,欢迎继续咨询!
更多推荐


所有评论(0)