LatchUtils:简化Java异步任务同步的利器

news/2025/10/14 14:07:24/文章来源:https://www.cnblogs.com/go-to-bed/p/19140965

转自https://mp.weixin.qq.com/s/p1nwmRl6-dyThqJ6StxC-A

在Java应用开发中,为了提升系统性能和响应速度,我们经常需要将一些耗时操作(如调用外部API、查询数据库、复杂计算等)进行异步并行处理。当主流程需要等待所有这些并行任务执行完毕后再继续时,我们通常会用到 ExecutorService、  CountDownLatch 等并发工具。
然而,直接使用这些原生工具,往往意味着需要编写一些重复的、模式化的“胶水代码”,这不仅增加了代码量,也让核心业务逻辑显得不够清晰。
为了解决这个问题,我封装了一个名为 LatchUtils 的轻量级工具类。它能够以一种极其简洁的方式来组织和管理这一类异步任务。

详细代码

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;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 的设计哲学是:多次提交,一次等待。

  • 任务注册: 在主流程代码中,可以先通过 LatchUtils.submitTask() 提交Runnable任务和其对应的Executor(该线程池用来执行这个Runnable)。
  • 执行并等待: 当并行任务都提交完毕后,你只需调用一次 LatchUtils.waitFor()。该方法会立即触发所有已注册任务的执行,并阻塞等待所有任务执行完成或超时。

API概览

这个工具类对外暴露的接口极其简单,只有两个核心静态方法:

submitTask()

public static void submitTask(Executor executor, Runnable runnable)

功能: 提交一个异步任务。

参数:

  • executor: java.util.concurrent.Executor - 指定执行此任务的线程池。
  • runnable: java.lang.Runnable - 需要异步执行的具体业务逻辑。

waitFor()

public static boolean waitFor(long timeout, TimeUnit timeUnit)

功能: 触发所有已提交任务的执行,并同步等待它们全部完成。

参数:

  • timeout: long - 最长等待时间。
  • timeUnit: java.util.concurrent.TimeUnit - 等待时间单位。

返回值:

  • true: 如果所有任务在指定时间内成功完成。
  • false: 如果等待超时。

注意: 该方法在执行后会自动清理当前线程提交的任务列表,因此可以重复使用。

实战实例

让我们来看一个典型的应用场景:一个聚合服务需要同时调用用户服务、订单服务和商品服务,拿到所有结果后再进行下一步处理。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;public class Main {    public static void main(String[] args) {
        // 1. 准备一个线程池
        ExecutorService executorService = Executors.newFixedThreadPool(3);        System.out.println("主流程开始,准备分发异步任务...");        // 2. 提交多个异步任务
        // 任务一:获取用户信息
        LatchUtils.submitTask(executorService, () -> {
            try {
                System.out.println("开始获取用户信息...");
                Thread.sleep(1000); // 模拟耗时
                System.out.println("获取用户信息成功!");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });        // 任务二:获取订单信息
        LatchUtils.submitTask(executorService, () -> {
            try {
                System.out.println("开始获取订单信息...");
                Thread.sleep(1500); // 模拟耗时
                System.out.println("获取订单信息成功!");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });        // 任务三:获取商品信息
        LatchUtils.submitTask(executorService, () -> {
            try {
                System.out.println("开始获取商品信息...");
                Thread.sleep(500); // 模拟耗时
                System.out.println("获取商品信息成功!");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });        System.out.println("所有异步任务已提交,主线程开始等待...");        // 3. 等待所有任务完成,最长等待5秒
        boolean allTasksCompleted = LatchUtils.waitFor(5, TimeUnit.SECONDS);        // 4. 根据等待结果继续主流程
        if (allTasksCompleted) {
            System.out.println("所有异步任务执行成功,主流程继续...");
        } else {
            System.err.println("有任务执行超时,主流程中断!");
        }        // 5. 关闭线程池
        executorService.shutdown();
    }
}

输出结果:

主流程开始,准备分发异步任务...
所有异步任务已提交,主线程开始等待...
开始获取商品信息...
开始获取用户信息...
开始获取订单信息...
获取商品信息成功!
获取用户信息成功!
获取订单信息成功!
所有异步任务执行成功,主流程继续...

从这个例子中可以看到,业务代码变得非常清晰。我们只需要关注“提交任务”和“等待结果”这两个动作,而无需关心 CountDownLatch 的初始化、countDown() 的调用以及异常处理等细节。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/936809.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qoder + ADB Supabase :5分钟GET超火AI手办生图APP

视频效果:一、前言 在AI原生应用开发的时代,传统的后端架构正在被重新定义。本文将带你体验如何使用 Qoder、阿里云ADB Supabase 和通义千问图像编辑模型(Qwen Image Edit),快速搭建一个完整的 AI 手办生图 Flutt…

实验报告2

实验1 问题1:srand(time(NULL))设置时间为随机种,保证每次随机数都不一样 问题2:在两个班中随机抽五个学号实验2 问题1:每次循环总价不清空,导致后面询问时数据出错 问题2:结束这一轮循环,跳过后面的语句;实验3 #inclu…

Agentic RAG对比传统RAG的优势

Agentic RAG 是传统检索增强生成(RAG)技术与AI智能体(Agent)自主能力相结合的一种进阶范式。它让RAG系统从一个遵循固定流程的“信息助手”,升级成了一个具备“大脑”、能够自主规划、决策并与多种工具交互的“智…

linux系统查看磁盘过程

1.查看磁盘信息:df -h2.查看一级文件信息:du -hd 13.查看数量前十的文件:du -a | sort -nr | head -n 104.ubuntu EasyConnect清除路由才能连本地其他的地址 route | grep 192.168.1. | grep tun1 |awk {print &quo…

2025-10-14 闲话

2025-10-14 闲话今天 yspm 21 岁了。 过去一年是超预期的,很难想象今年不是窝在宿舍床上编辑这段话。集体生活的挣扎在一天一顿大融城中彻底转变了形态。 时间过得很快,20岁不会再回来,野蛮荒唐不会再回来。但是耳畔…

ftp多用户多目录配置

ftp多用户多目录配置 测试环境:centos7 1. 装包与卸载yum -y install vsftpd yum -y autoremove vsftpd&&rm -rf /etc/vsftpd /etc/pam.d/vsftpd.rpmsave2. 用户清单配置 不要去动user_list和ftpusers文件,这…

芋道框架怎么样

最近工作中使用了一个java的后端框架叫做 芋道简单说一下我对他的理解和认识吧1.完美的前后端代码自动生成 2.完美的组件集成,包括工作流,ai,自动任务,文件管理器,mq等等缺点: 1.这个框架会挑选你的笔记本,如果…

神级掩护软件!老板路过我电脑在“系统更新中”

软件介绍 一键“假装电脑崩溃”神器来袭! 无论是蓝屏死机、FBI警告、系统更新卡死、还是无限重启,这款小巧的恶搞模拟器都能以假乱真地还原现场,让你轻松度过那些“不想被打扰”的时刻。 体积仅 7MB,却集成了多种经…

超真实“电脑崩溃模拟器”:蓝屏、重启、FBI警告一应俱全!

软件介绍 一键“假装电脑崩溃”神器来袭! 无论是蓝屏死机、FBI警告、系统更新卡死、还是无限重启,这款小巧的恶搞模拟器都能以假乱真地还原现场,让你轻松度过那些“不想被打扰”的时刻。 体积仅 7MB,却集成了多种经…

(20)ASP.NET Core2.2 EF创建模型(必需属性和可选属性、最大长度、并发标记、阴影属性) - 指南

(20)ASP.NET Core2.2 EF创建模型(必需属性和可选属性、最大长度、并发标记、阴影属性) - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !…

(在构造函数中)调用super(props)的目的是什么?

在 React 类组件 中,constructor 构造函数里调用 super(props) 是一个非常经典的机制, 一、基本背景 在 React 中,我们定义类组件时通常这样写: class MyComponent extends React.Component {constructor(props) {…

温故知新,机器人进化论,机器人分类与全球格局

机器人基本概念与范畴 “机器人”是一个比较宽泛的概念,不同文献和标准下的定义稍有差异。一般来说,机器人是能感知环境、作出决策或规划、并作用于环境的机械系统(带有驱动、传感、控制、算法等)的一类设备。 在工…

Zemax:初学者的混合模式 - 指南

Zemax:初学者的混合模式 - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco&qu…

【无标题】使用 Playwright 实现跨 Chromium、Firefox、WebKit 浏览器自动化管理

【无标题】使用 Playwright 实现跨 Chromium、Firefox、WebKit 浏览器自动化管理pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-…

西门子博图软件TIA V18使用PLCSIM Advanced V5.0进行仿真与其他程序进行通讯

由于标准版PLCSIM只是PLC内部通讯用,若要与外部进行通讯,需要安装Advanced版本,TIA V18的兼容advanced版本需要在V5.0以上,这里使用了V5.0版本。 PLCSIM Advanced V5.0安装过程中可能会有兼容性之类的报错 发生该…

MyEclipse 2017/2018 安装与破解 图文教程

SSM 框架-02-MyEclipse 2017/2018 安装与破解 现在在学J2EE,然后使用的工具就是 MyEclipse,现在就抛弃 Eclipse 了,我就不多说它俩的区别了,但是 MyEclipse 是收费的,下面介绍 MyEclipse 2017 安装与破解 一、下载…

面向对象初级

面向对象初级:1.面向对象三大特征:封装  继承   多态    self含义:  指向实例对象本身,让实例能够访问类中的属性和方法2.类和对象的创建:    类和对象的创建(登录注册功能) class User:def __in…

【文章目录】

文章目录 【STM32】 【STM32系列】STM32通用【STM32系列】基于HAL库的串口DMA空闲中断接收+串口DMA发送 【STM32系列】超好用的开源按键状态系统lwbtn,以及超详细的移植教程 【STM32系列】EXTI11触发三ADC同步(并非同…

Excel DDE 教學:即時資料交換的詳細指南 - 指南

Excel DDE 教學:即時資料交換的詳細指南 - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", …

子网掩码基础知识

子网掩码基础知识 子网掩码由连续的1和连续的0组成,1表示网络位,0表示主机位。 1. 基础概念IPv4地址:由32位二进制组成,通常表示为4个十进制数(如192.168.1.0)。 子网掩码:用于划分网络和主机部分。例如,255.2…