专业的JAVA编程教程与资源

网站首页 > java教程 正文

LatchUtils: 让Java异步任务更简洁更优雅

temp10 2025-06-30 19:57:51 java教程 2 ℃ 0 评论

在Java应用开发中,我们经常需要将一些任务进行异步并行处理。当主流程需要等待所有任务执行完毕后再继续时,这就会用到 CountDownLatch 等并发工具类。

然而,直接使用这些原生工具类,往往意味着需要编写一些重复的、模式化的“胶水代码”,这不仅增加了代码量,也让核心业务逻辑显得不够清晰。

LatchUtils: 让Java异步任务更简洁更优雅

我封装了一个名为 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;
        }
    }
}


使用说明

只需如下两步即可。

  1. 提交任务: 在主流程代码,先调用 LatchUtils.submitTask() 提交Runnable任务和其对应的Executor(用来执行这个Runnable)。
  2. 执行并等待: 当并行任务都提交完毕后,只需调用一次 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,仅返回布尔值。

复杂。需要显式地在finallycountDown(),并为主线程的await()处理InterruptedException

关注点分离

优秀。开发者只需关注“提交”和“等待”两个动作。

一般。并发控制逻辑(countDown())侵入到了业务Runnable中。

易用性

非常简单。几乎没有学习成本。

需要理解CountDownLatch。容易忘记countDown()或错误处理。

结论很明显

对于“分发一组并行任务,然后等待它们全部完成”这一特定但常见的模式,LatchUtils 通过适度的封装,极大地简化了开发者的工作。

它隐藏了并发控制的复杂性,让业务代码回归其本质,让代码更简洁优雅。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表