以实际案例说明CountDownLatch和CyclicBarrier的使用场景
我来用两个生动的实际案例来说明CountDownLatch和CyclicBarrier的使用场景。
案例1:使用CountDownLatch的电商订单处理系统
场景描述
想象一个电商平台的订单处理流程。当用户下单时,系统需要完成多个独立的任务才能确认订单:库存检查、支付验证、优惠券验证、物流信息校验等。主线程需要等待所有这些验证都完成才能继续处理。
代码实现
public class OrderProcessingSystem {
public static void main(String[] args) throws InterruptedException {
// 创建一个CountDownLatch,计数值为4(表示4个验证任务)
CountDownLatch orderLatch = new CountDownLatch(4);
System.out.println("开始处理订单...");
long startTime = System.currentTimeMillis();
// 启动库存检查线程
new Thread(() -> {
try {
Thread.sleep(2000); // 模拟检查耗时
System.out.println("库存检查完成,库存充足");
orderLatch.countDown(); // 完成一个任务,计数减1
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "库存检查线程").start();
// 启动支付验证线程
new Thread(() -> {
try {
Thread.sleep(3000); // 模拟验证耗时
System.out.println("支付验证完成,支付成功");
orderLatch.countDown();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "支付验证线程").start();
// 启动优惠券验证线程
new Thread(() -> {
try {
Thread.sleep(1500); // 模拟验证耗时
System.out.println("优惠券验证完成,优惠券有效");
orderLatch.countDown();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "优惠券验证线程").start();
// 启动物流信息校验线程
new Thread(() -> {
try {
Thread.sleep(1000); // 模拟校验耗时
System.out.println("物流信息校验完成,可以配送");
orderLatch.countDown();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "物流校验线程").start();
// 主线程等待所有验证任务完成
orderLatch.await();
long endTime = System.currentTimeMillis();
System.out.println("所有验证任务已完成,订单处理成功!");
System.out.println("总耗时:" + (endTime - startTime) + "ms");
}
}
运行输出示例
开始处理订单...
物流信息校验完成,可以配送
优惠券验证完成,优惠券有效
库存检查完成,库存充足
支付验证完成,支付成功
所有验证任务已完成,订单处理成功!
总耗时:3024ms
案例2:使用CyclicBarrier的大数据批处理系统
场景描述
想象一个需要处理大量数据的系统,比如一个日志分析系统。我们需要多个线程并行处理数据,每个线程负责一部分数据。处理完一批数据后,需要等待所有线程都完成才能进行数据汇总,然后继续处理下一批数据。
代码实现
public class LogAnalysisSystem {
public static void main(String[] args) {
int threadCount = 3; // 3个工作线程
int batchSize = 1000; // 每批处理1000条日志
AtomicInteger totalProcessedLogs = new AtomicInteger(0);
// 创建CyclicBarrier,所有线程到达后执行汇总任务
CyclicBarrier barrier = new CyclicBarrier(threadCount, () -> {
System.out.println("\n=== 当前批次所有数据处理完成 ===");
System.out.println("已处理总日志数:" + totalProcessedLogs.get());
System.out.println("开始处理下一批数据...\n");
});
// 启动工作线程
for (int i = 0; i < threadCount; i++) {
final int threadId = i + 1;
new Thread(() -> {
try {
// 模拟处理多批数据
for (int batch = 1; batch <= 3; batch++) {
// 模拟处理一批数据
int processedCount = processLogsInBatch(threadId, batchSize);
totalProcessedLogs.addAndGet(processedCount);
System.out.printf("线程%d完成第%d批数据处理,本批处理%d条日志\n",
threadId, batch, processedCount);
// 等待其他线程完成当前批次
barrier.await();
}
} catch (InterruptedException | BrokenBarrierException e) {
Thread.currentThread().interrupt();
}
}, "工作线程-" + threadId).start();
}
}
// 模拟处理一批日志的方法
private static int processLogsInBatch(int threadId, int batchSize) throws InterruptedException {
// 模拟处理时间
Thread.sleep((long) (Math.random() * 2000));
// 返回实际处理的日志数量
return (int) (batchSize * (0.8 + Math.random() * 0.4));
}
}
运行输出示例
线程1完成第1批数据处理,本批处理923条日志
线程2完成第1批数据处理,本批处理1012条日志
线程3完成第1批数据处理,本批处理967条日志
=== 当前批次所有数据处理完成 ===
已处理总日志数:2902
开始处理下一批数据...
线程2完成第2批数据处理,本批处理889条日志
线程1完成第2批数据处理,本批处理945条日志
线程3完成第2批数据处理,本批处理978条日志
=== 当前批次所有数据处理完成 ===
已处理总日志数:5714
开始处理下一批数据...
关键区别总结
CountDownLatch(订单处理案例)适用场景
- 一次性的等待场景。
- 主线程等待多个子任务完成。
- 任务之间相对独立。
- 不需要重复使用的场景。
CyclicBarrier(日志分析案例)适用场景
- 需要循环处理的场景。
- 多个线程需要相互等待。
- 处理完一个阶段后,需要进行汇总或同步。
- 需要重复使用的场景。
通过这两个实际的案例,我们可以看到:
- CountDownLatch更像是“等待结束”的工具,适合主线程等待多个任务完成的场景。
- CyclicBarrier更像是“集合点”的工具,适合多个线程之间互相等待,且需要多次重复的场景。
这样理解起来是不是更清晰了?如果你还有任何疑问,欢迎评论区留言讨论。

1339

被折叠的 条评论
为什么被折叠?



