网站首页 > java教程 正文
在Java应用开发中,我们经常需要将一些任务进行异步并行处理。当主流程需要等待所有任务执行完毕后再继续时,这就会用到 CountDownLatch 等并发工具类。
然而,直接使用这些原生工具类,往往意味着需要编写一些重复的、模式化的“胶水代码”,这不仅增加了代码量,也让核心业务逻辑显得不够清晰。
我封装了一个名为 LatchUtils 的轻量级工具类。它能够以一种极其简洁的方式来组织和管理这一类异步任务。
详细代码
先上代码,后面会有使用说明和示例以及和传统实现代码的对比
public class LatchUtils {
private static final ThreadLocal<List<TaskInfo>> THREADLOCAL = ThreadLocal.withInitial(LinkedList::new);
public static void submitTask(Executor executor, Runnable runnable) {
THREADLOCAL.get().add(new TaskInfo(executor, runnable));
}
private static List<TaskInfo> popTask() {
List<TaskInfo> taskInfos = THREADLOCAL.get();
THREADLOCAL.remove();
return taskInfos;
}
public static boolean waitFor(long timeout, TimeUnit timeUnit) {
List<TaskInfo> taskInfos = popTask();
if (taskInfos.isEmpty()) {
return true;
}
CountDownLatch latch = new CountDownLatch(taskInfos.size());
for (TaskInfo taskInfo : taskInfos) {
Executor executor = taskInfo.executor;
Runnable runnable = taskInfo.runnable;
executor.execute(() -> {
try {
runnable.run();
} finally {
latch.countDown();
}
});
}
boolean await = false;
try {
await = latch.await(timeout, timeUnit);
} catch (Exception ignored) {
}
return await;
}
private static final class TaskInfo {
private final Executor executor;
private final Runnable runnable;
public TaskInfo(Executor executor, Runnable runnable) {
this.executor = executor;
this.runnable = runnable;
}
}
}
使用说明
只需如下两步即可。
- 提交任务: 在主流程代码,先调用 LatchUtils.submitTask() 提交Runnable任务和其对应的Executor(用来执行这个Runnable)。
- 执行并等待: 当并行任务都提交完毕后,只需调用一次 LatchUtils.waitFor()。该方法会立即触发所有已注册任务执行,并阻塞等待所有任务执行完成或超时。
实战示例
ExecutorService executorService = Executors.newFixedThreadPool(3);
LatchUtils.submitTask(executorService, () -> {
System.out.println("Task 1 started.");
// Simulate some work
System.out.println("Task 1 completed.");
});
LatchUtils.submitTask(executorService, () -> {
System.out.println("Task 2 started.");
// Simulate some work
System.out.println("Task 2 completed.");
});
LatchUtils.submitTask(executorService, () -> {
System.out.println("Task 3 started.");
// Simulate some work
System.out.println("Task 3 completed.");
});
boolean waitFor = LatchUtils.waitFor(5, TimeUnit.SECONDS);
if (waitFor) {
System.out.println("All tasks completed within the specified time.");
} else {
System.out.println("Some tasks may not have completed.");
}
从这个例子中可以看到,业务代码变得非常清晰。我们只需要关注“提交任务”和“等待结果”这两个动作,而无需关心 CountDownLatch 的初始化、countDown() 的调用等细节。
对比:不使用 LatchUtils
为了更好地理解 LatchUtils 带来的价值,让我们用传统的Java并发API实现与上面完全相同的功能。
ExecutorService executorService = Executors.newFixedThreadPool(3);
CountDownLatch latch = new CountDownLatch(3);
executorService.execute(() -> {
try {
System.out.println("Task 1 started.");
// Simulate some work
System.out.println("Task 1 completed.");
} finally {
latch.countDown();
}
});
executorService.execute(() -> {
try {
System.out.println("Task 2 started.");
// Simulate some work
System.out.println("Task 2 completed.");
} finally {
latch.countDown();
}
});
executorService.execute(() -> {
try {
System.out.println("Task 3 started.");
// Simulate some work
System.out.println("Task 3 completed.");
} finally {
latch.countDown();
}
});
boolean await = false;
try {
await = latch.await(5, TimeUnit.SECONDS);
} catch (Exception e) {
}
if (await) {
System.out.println("All tasks completed within the specified time.");
} else {
System.out.println("Some tasks may not have completed.");
}
对比分析
特性 | LatchUtils | 手动 CountDownLatch |
代码简洁性 | 极高。业务逻辑和并发控制分离,核心代码清晰。 | 中等。需要在每个任务中嵌入latch.countDown(),分散了关注点。 |
状态管理 | 自动。工具类内部自动管理CountDownLatch。 | 手动。需要自己创建、维护和传递CountDownLatch实例。 |
错误处理 | 简化。waitFor内部处理InterruptedException,仅返回布尔值。 | 复杂。需要显式地在finally中countDown(),并为主线程的await()处理InterruptedException。 |
关注点分离 | 优秀。开发者只需关注“提交”和“等待”两个动作。 | 一般。并发控制逻辑(countDown())侵入到了业务Runnable中。 |
易用性 | 非常简单。几乎没有学习成本。 | 需要理解CountDownLatch。容易忘记countDown()或错误处理。 |
结论很明显:
对于“分发一组并行任务,然后等待它们全部完成”这一特定但常见的模式,LatchUtils 通过适度的封装,极大地简化了开发者的工作。
它隐藏了并发控制的复杂性,让业务代码回归其本质,让代码更简洁优雅。
猜你喜欢
- 2025-06-30 CompletableFuture实现异步编排(异步编排是什么)
- 2025-06-30 SpringBoot如何优雅的实现异步调用?
- 2025-06-30 你知道@Async 是怎么让方法异步执行的吗?
- 2025-06-30 java异步编程难题拆解(java异步编程的四种方法)
- 2025-06-30 带你玩转CompletableFuture异步编程
- 2025-06-30 JDK21 虚拟线程原理剖析与性能深度解析
- 2025-06-30 Java 并发编程 11 - 异步执行框架 Executor
- 2025-06-30 JAVA8异步编程注意事项(java异步实现原理)
- 2025-06-30 Java响应式编程:异步非阻塞之道与实战框架解析
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)