CountDownLatch
是 Java 并发包中的一个同步工具类,它在生产环境中有着广泛的应用。主要用于协调多个线程之间的活动,确保一个或多个线程等待直到其他线程完成一组预定义的任务后才继续执行。以下是 CountDownLatch
在生产环境中的几种典型应用场景及其应用方式:
1. 应用程序初始化
在启动大型分布式系统或复杂的应用程序时,可能需要加载多种资源(如数据库连接、缓存、配置文件等)。可以使用 CountDownLatch
来确保所有资源都加载完毕后再开始服务请求。
import java.util.concurrent.CountDownLatch;
public class AppInitialization {
public static void main(String[] args) throws InterruptedException {
int tasks = 3; // 假设有三个初始化任务
CountDownLatch latch = new CountDownLatch(tasks);
new Thread(new InitializationTask("Database", latch)).start();
new Thread(new InitializationTask("Cache", latch)).start();
new Thread(new InitializationTask("Configuration", latch)).start();
System.out.println("Waiting for all services to initialize...");
latch.await(); // 等待所有初始化任务完成
System.out.println("All services initialized, application is ready.");
}
static class InitializationTask implements Runnable {
private final String name;
private final CountDownLatch latch;
public InitializationTask(String name, CountDownLatch latch) {
this.name = name;
this.latch = latch;
}
@Override
public void run() {
try {
System.out.println(name + " service initializing...");
Thread.sleep(1000); // 模拟初始化时间
System.out.println(name + " service initialized.");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
latch.countDown(); // 完成后减少计数器
}
}
}
}
2. 并行计算
当需要将一个大任务分解为多个小任务并行执行时,CountDownLatch
可以用来确保主程序在所有子任务完成后才能进行结果的汇总和后续处理。
import java.util.concurrent.CountDownLatch;
public class ParallelComputation {
public static void main(String[] args) throws InterruptedException {
int taskCount = 5; // 假设要执行五个并行任务
CountDownLatch latch = new CountDownLatch(taskCount);
for (int i = 0; i < taskCount; i++) {
new Thread(new Task(latch, i)).start();
}
latch.await(); // 主线程等待所有任务完成
System.out.println("所有任务已完成");
}
static class Task implements Runnable {
private final CountDownLatch latch;
private final int taskId;
public Task(CountDownLatch latch, int taskId) {
this.latch = latch;
this.taskId = taskId;
}
@Override
public void run() {
try {
System.out.println("任务 " + taskId + " 开始执行");
Thread.sleep((long)(Math.random() * 2000)); // 模拟任务执行时间
System.out.println("任务 " + taskId + " 执行完成");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
latch.countDown(); // 减少计数器
}
}
}
}
3. 测试框架中的并发测试
在编写自动化测试用例时,有时需要验证多线程环境下某些条件是否成立。可以使用 CountDownLatch
来确保所有测试线程都完成了各自的准备工作后,再检查最终状态。
import java.util.concurrent.CountDownLatch;
public class ConcurrentTestExample {
public static void main(String[] args) throws InterruptedException {
int testThreadCount = 4; // 测试线程数量
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(testThreadCount);
for (int i = 0; i < testThreadCount; ++i) // 创建并启动线程
new Thread(new Worker(startSignal, doneSignal)).start();
System.out.println("准备所有测试线程...");
Thread.sleep(1000); // 模拟准备时间
startSignal.countDown(); // 放行所有测试线程
System.out.println("所有测试线程开始执行...");
doneSignal.await(); // 等待所有测试线程完成
System.out.println("所有测试线程已完成");
}
static class Worker implements Runnable {
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;
Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
this.startSignal = startSignal;
this.doneSignal = doneSignal;
}
public void run() {
try {
startSignal.await(); // 等待信号放行
doWork(); // 执行实际工作
doneSignal.countDown(); // 标记自己已经完成
} catch (InterruptedException ex) {}
}
void doWork() {
System.out.println(Thread.currentThread().getName() + " 正在执行测试");
try {
Thread.sleep((long)(Math.random() * 2000)); // 模拟工作时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
4. 关闭顺序管理
在一些情况下,比如关闭服务器或清理资源时,需要确保所有子系统或组件按正确的顺序关闭。CountDownLatch
可以用来控制这种顺序,确保所有必要的清理操作都已完成后再完全退出程序。
import java.util.concurrent.CountDownLatch;
public class ShutdownSequence {
public static void main(String[] args) throws InterruptedException {
int subsystems = 3; // 假设有三个子系统需要关闭
CountDownLatch latch = new CountDownLatch(subsystems);
new Thread(new SubsystemShutdown("Subsystem A", latch)).start();
new Thread(new SubsystemShutdown("Subsystem B", latch)).start();
new Thread(new SubsystemShutdown("Subsystem C", latch)).start();
latch.await(); // 等待所有子系统关闭
System.out.println("所有子系统已关闭,程序退出");
}
static class SubsystemShutdown implements Runnable {
private final String name;
private final CountDownLatch latch;
public SubsystemShutdown(String name, CountDownLatch latch) {
this.name = name;
this.latch = latch;
}
@Override
public void run() {
try {
System.out.println(name + " 正在关闭...");
Thread.sleep(1000); // 模拟关闭过程
System.out.println(name + " 已关闭");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
latch.countDown(); // 关闭后减少计数器
}
}
}
}
总结
CountDownLatch
在生产环境中主要应用于那些需要协调多个线程之间活动的场景。通过提供一种简单而有效的机制来确保一个或多个线程等待直到其他线程完成特定任务,CountDownLatch
增强了并发编程的能力,使得开发者能够更容易地构建高效且可靠的并发系统。无论是应用程序的初始化、并行计算、测试框架还是系统关闭流程,CountDownLatch
都是一个非常有用的工具。