当前位置: 首页 > article >正文

JAVA将一个同步方法改为异步执行

目的:

这么做的目的就是为了使一个高频率执行的方法能不阻塞整个程序,将该方法丢入到线程池中让线程去做异步执行,既提高了程序整体运行速度,也使得在高并发环境下程序能够更加健壮(同步执行可能会使得请求堆积以致系统瘫痪,而异步的话,并发过高消耗完默认线程最大值后,后面来的请求直接执行拒绝策略就可以了)

背景:

项目中有一个监控推送的方法,每5分钟执行一次,且随着堆积数据量越来越大,推送时做的一些数据库交互逻辑耗时越来越长,平台统计的该组件交易耗时就越来越高。
老大肯定是不希望每周平台周报公布时自己管理的组件数据不好看的,且这是一个监控功能,不影响业务,那就更不该喧宾夺主了。综上所述决定将其方法由同步改成异步。

代码:

先定义一个单例模式的线程池管理器MonitorExecutor类。

/*** class MonitorExecutor*/
public class MonitorExecutor implements Disposable {private static final Logger log = LoggerFactory.getLogger(MonitorExecutor.class);private static final Object MONITOR_LOCK = new Object();private volatile ExecutorService monitorProcessor;private static class SingletonHolder {private static final MonitorExecutor INSTANCE = new MonitorExecutor();}public static MonitorExecutor getInstance() {return SingletonHolder.INSTANCE;}private ExecutorService getMonitorProcessor() {if (monitorProcessor == null) {synchronized (MONITOR_LOCK) {if (monitorProcessor == null) {MonitorPoolConfig config = Configuration.loader(MonitorPoolConfig.class);int minPoolSize = config.getMinPoolSize();int maxPoolSize = config.getMaxPoolSize();monitorProcessor = new ThreadPoolExecutor(minPoolSize, maxPoolSize, 60L, TimeUnit.SECONDS,new SynchronousQueue<>(),new NamedThreadFactory("monitor-processing-thread"),new ThreadPoolExecutor.CallerRunsPolicy());ShutdownHook.getInstance().addDisposable(this);log.info("Initialize the monitor processing thread pool, minPoolSize {}, maxPoolSize {}", minPoolSize, maxPoolSize);}}}return monitorProcessor;}private static ExecutorService getExecutor(){return getInstance().getMonitorProcessor();}public static void execute(Runnable cmd) {getExecutor().execute(cmd);}@Overridepublic void destroy() {if (monitorProcessor != null) {monitorProcessor.shutdown();}}
}

定义一个线程池大小配置类MonitorPoolConfig,可用默认值也可在application里动态设置。关于@Config动态读取配置文件请参见我另一篇文章。

/*** class MonitorPoolConfig*/
@Data
@ConfigPrefix(ConfKeyConstants.SERVER_MONITOR_POOL)
public class MonitorPoolConfig {@ConfigMapping(key = "minPoolSize", def = "5")private Integer minPoolSize;@ConfigMapping(key = "maxPoolSize", def = "200")private Integer maxPoolSize;}

OK准备工作已做完,现在先来看一下优化前的方法代码:

    public boolean save(MonitorInfoDTO dto) {if (dto == null) {return false;}// 推送的四个步骤,我想将前三个步骤改成异步,最后一个推送错误告警的依然同步progressDoService.saveProgress(converter.toProgressEntities(dto.getProgresses()));seqGenStatusDoService.saveStatus(converter.toSeqGenStatusEntities(dto.getSeqGenstatuses()));workerIdDoService.saveWorkerIds(converter.toWorkerIdEntities(dto.getWorkerIdInfos()));failedSeqDoService.saveFailedSeqs(converter.toFailedSeqEntities(dto.getFailedSeqs()));return true;}

优化改造后:

    // 将异步执行抽象出一个方法private <T> void saveAsync(List<T> list, Function<List<T>, Boolean> func) {MonitorExecutor.execute(() -> {func.apply(list);});}public boolean save(MonitorInfoDTO dto) {if (dto == null) {return false;}saveAsync(converter.toProgressEntities(dto.getProgresses()),progresses -> progressDoService.saveProgress(progresses));saveAsync(converter.toSeqGenStatusEntities(dto.getSeqGenstatuses()),statuses -> seqGenStatusDoService.saveStatus(statuses));saveAsync(converter.toWorkerIdEntities(dto.getWorkerIdInfos()),workerIdInfos -> workerIdDoService.saveWorkerIds(workerIdInfos));failedSeqDoService.saveFailedSeqs(converter.toFailedSeqEntities(dto.getFailedSeqs()));return true;}

在saveAsync里可以随意定制异步执行时的共用操作,就像切面思维一样,例如:

    // 将异步执行抽象出一个方法private <T> void saveAsync(List<T> list, Function<List<T>, Boolean> func) {final String systemId = SystemContext.getSystemId();final String tenantId = SystemContext.getTenantId();MonitorExecutor.execute(() -> {SystemContext.setSystemId(systemId);SystemContext.setTenantId(tenantId);try {func.apply(list);} finally {SystemContext.clear();}});}

优化完毕。


线程池管理器的AI解析说明:

这段代码定义了一个名为 MonitorExecutor 的类,它是一个单例模式的线程池管理器,用于处理监控任务。以下是对代码的详细分析:

  1. 类和接口
    类名:MonitorExecutor
    实现接口:Disposable,表示该类实现了资源释放接口,用于在程序关闭时释放资源。
  2. 日志记录
    使用了 org.slf4j.Logger 和 LoggerFactory 来记录日志,便于调试和监控。
  3. 单例模式
    使用了双重检查锁定(Double-Check Locking)的单例模式实现:
    静态内部类 SingletonHolder 包含一个静态实例 INSTANCE,确保线程安全。
    getInstance() 方法返回单例实例。
  4. 线程池初始化
    线程池的初始化:
    使用了 ThreadPoolExecutor 来创建线程池。
    配置参数从 MonitorPoolConfig 类中加载,包括最小线程数 minPoolSize 和最大线程数 maxPoolSize。
    使用了 SynchronousQueue 作为任务队列,这意味着任务会直接提交给线程执行,如果没有空闲线程,则会拒绝任务。
    使用了 NamedThreadFactory 来为线程池中的线程命名,便于调试和监控。
    使用了 CallerRunsPolicy 作为拒绝策略,当任务被拒绝时,会在调用线程中执行任务。
  5. 线程池的生命周期管理
    初始化:
    在 getMonitorProcessor() 方法中,如果 monitorProcessor 为 null,则会初始化线程池。
    初始化后,将当前实例添加到 ShutdownHook 中,确保在程序关闭时能够正确释放资源。
    销毁:
    实现了 Disposable 接口的 destroy() 方法,在程序关闭时调用 shutdown() 方法来关闭线程池。
  6. 任务提交
    提供了 execute(Runnable cmd) 方法,用于提交任务到线程池中执行。
  7. 代码结构
    线程安全:使用了 synchronized 块和双重检查锁定来确保线程池的初始化是线程安全的。
    日志记录:在初始化线程池时记录了日志,便于监控线程池的初始化状态。
    资源管理:通过实现 Disposable 接口,确保在程序关闭时能够正确释放线程池资源。
  8. 改进建议
    线程池配置:
    当前使用了 SynchronousQueue,这可能导致任务提交时的阻塞。如果任务提交频率较高,可以考虑使用其他类型的阻塞队列,如 LinkedBlockingQueue。
    日志记录:
    在 destroy() 方法中添加日志记录,记录线程池的销毁状态。
    异常处理:
    在 execute(Runnable cmd) 方法中添加异常处理逻辑,确保任务提交失败时能够记录错误信息。
  9. 示例用法
public class Main {public static void main(String[] args) {// 提交任务到线程池MonitorExecutor.execute(() -> {System.out.println("Task is running in thread: " + Thread.currentThread().getName());});// 程序关闭时释放资源Runtime.getRuntime().addShutdownHook(new Thread(() -> {MonitorExecutor.getInstance().destroy();}));}
}

通过这段代码,你可以更好地理解和使用 MonitorExecutor 类来管理监控任务的线程池。


OK码完收工,THX!

相关文章:

JAVA将一个同步方法改为异步执行

目的&#xff1a; 这么做的目的就是为了使一个高频率执行的方法能不阻塞整个程序&#xff0c;将该方法丢入到线程池中让线程去做异步执行&#xff0c;既提高了程序整体运行速度&#xff0c;也使得在高并发环境下程序能够更加健壮&#xff08;同步执行可能会使得请求堆积以致系…...

安装jdk步骤

将Linux安装jdk的步骤放入shell脚本中 #!/bin/bash # 阿里云服务器专用 - 全自动安装 OpenJDK 1.8&#xff08;无交互&#xff09; # 仅支持 yum 系系统&#xff08;CentOS/RHEL/Alibaba Cloud Linux&#xff09;# 检查 root 权限 if [ "$(id -u)" -ne 0 ]; thenech…...

HTML应用指南:利用POST请求获取全国德邦快递服务网点位置信息

德邦快递作为中国领先的综合性物流服务提供商,自1996年成立以来,始终致力于为客户提供高效、安全的大件快递及其他物流解决方案。德邦快递凭借其强大的直营模式、“最后一公里”的优质服务以及对科技的持续投入,在竞争激烈的物流市场中占据了重要位置。特别是在大件快递领域…...

高级可视化图表分析实践——以《大侠立志传》武器系统为例

高级可视化图表分析实践——以《大侠立志传》武器系统为例 引言武器类型分布矩形树图结论 不同品质/类别武器的攻击力分布情况蜂群图分析结论 武器来源桑基图分析结论 武器附加属性词云图分析结论 不同品级武器装备熟练度要求/特质要求离散热力图结论品质与熟练度的正相关性品质…...

RoPE长度外推:外插内插

RoPE:假定 α \alpha α是定值 其中一半位置是用cos表示的 cos ⁡ ( k α − 2 i d ) \cos(k\alpha^{-\frac{2i}{d}}) cos(kα−d2i​)(另一半是sin)(d是词嵌入维度) 当太长如何解决: 1 直接不管—外插 缺点:超过一定长度性能急剧下降。(较大时&#xff0c;对应的很多位置编码…...

【C++进阶】第2课—多态

文章目录 1. 认识多态2. 多态的定义和实现2.1 构成多态的必要条件2.2 虚函数2.3 虚函数的重写或覆盖2.4 协变(了解)2.5 析构函数的重写2.6 override和final关键字2.7 重载、重写、隐藏对比 3. 纯虚函数和抽象类4. 多态原理4.1 虚函数表指针4.2 多态的实现4.3 静态绑定和动态绑定…...

RSS 2025|斯坦福提出「统一视频行动模型UVA」:实现机器人高精度动作推理

导读 在机器人领域&#xff0c;让机器人像人类一样理解视觉信息并做出精准行动&#xff0c;一直是科研人员努力的方向。今天&#xff0c;我们要探讨的统一视频行动模型&#xff08;Unified Video Action Model&#xff0c;UVA&#xff09;&#xff0c;就像给机器人装上了一个“…...

AWS IoT Core与MSK集成实战:打造高可靠实时IoT数据管道

在物联网快速发展的今天,如何高效、安全地处理海量设备数据成为企业面临的一大挑战。本文将带您深入探索AWS IoT Core与Amazon MSK(Managed Streaming for Apache Kafka)的集成方案,手把手教您搭建一个可靠、可扩展的实时IoT数据处理管道。无论您是IoT开发者、大数据工程师还是…...

第十六届蓝桥杯B组第二题

当时在考场的时候这一道题目 无论我是使用JAVA的大数&#xff08;BIGTHGER&#xff09;还是赛后 使用PY 都是没有运行出来 今天也是突发奇想在B站上面搜一搜 看了才知道这也是需要一定的数学思维 通过转换 设X来把运算式精简化 避免运行超时 下面则是代码 public class lanba…...

Android Studio 中使用 SQLite 数据库开发完整指南(Kotlin版本)

文章目录 1. 项目准备1.1 创建新项目1.2 添加必要依赖 2. 数据库设计3. 实现数据库3.1 创建实体类 (Entity)3.2 创建数据访问对象 (DAO)3.3 创建数据库类 4. 创建 Repository5. 创建 ViewModel6. 实现 UI 层6.1 创建笔记列表 Activityactivity_notes_list.xmlNotesListActivity…...

Spring 框架实战:如何实现高效的依赖注入,优化项目结构?

Spring 框架实战&#xff1a;如何实现高效的依赖注入&#xff0c;优化项目结构&#xff1f; 在当今的 Java 开发领域&#xff0c;Spring 框架占据着举足轻重的地位。而依赖注入作为 Spring 的核心概念之一&#xff0c;对于构建高效、灵活且易于维护的项目结构有着关键作用。本…...

C++ learning day 01

目录 1. iostream : 2.第一个C++程序 3. 执行过程以及以上例子详解(以上例子为参考) 1. iostream : 全称: input/output stream library 作用: 用于处理输入输出操作 2.第一个C++程序 #include <iostream>int main() {std::cout << "Hello World! &qu…...

李沐《动手学深度学习》 | 多层感知机

文章目录 感知机模型《深度学习入门》的解释训练感知机损失函数的选择感知机的收敛定理&#xff1a;什么时候能够停下来&#xff0c;是不是真的可以停下来感知机的不足 多层感知模型案例引入隐藏层从线性到非线性单隐藏层-单分类案例多隐藏层 激活函数softmax函数溢出的问题 多…...

Windows 下 MongoDB 安装指南

&#x1f6d2; 第一步&#xff1a;获取 MongoDB 安装包 访问官网大本营&#xff1a;打开浏览器&#xff0c;直奔 MongoDB 官网下载页面&#xff08;就像逛淘宝一样简单&#xff09; 挑选心仪的版本&#xff1a; 在 "Select Version" 选择最新稳定版&#xff08;新手…...

vue教程(vuepress版)

Vue 完全指南 项目介绍 这是一个系统化的 Vue.js 学习教程&#xff0c;采用循序渐进的方式&#xff0c;帮助开发者从零开始掌握 Vue 开发技能。 教程特点 循序渐进: 从 Vue 基础概念开始&#xff0c;逐步深入到高级特性&#xff0c;适合不同层次的开发者学习实战驱动: 结合…...

【网络原理】深入理解HTTPS协议

本篇博客给大家带来的是网络原理的知识点,本篇解释了为什么有HTTP还要发展HTTPS协议. &#x1f40e;文章专栏: JavaEE初阶 &#x1f680;若有问题 评论区见 ❤ 欢迎大家点赞 评论 收藏 分享 如果你不知道分享给谁,那就分享给薯条. 你们的支持是我不断创作的动力 . 王子,公主请阅…...

Linux上将conda环境VLLM服务注册为开机自启

这里写目录标题 一、Systemd服务方式1、编写启动脚本2、保存脚本并赋予权限3、创建 systemd 服务单元文件3、 启用并测试服务4、停止systemd服务 二、Crontab方式1、编辑crontab文件2、添加开机启动任务 参考链接 项目需要vllm进行模型支撑&#xff0c;所以需要做成开机自启保证…...

如何快速开始一个前端项目

如何快速开始一个前端项目 第一步&#xff1a;下载 NVM 这个是 Node 的一个版本管理工具&#xff0c;下载下来以后可以对 Node 轻松进行版本管理。 具体下载步骤如下链接&#xff1a;nvm&#xff1a;Node.js版本管理工具的安装与使用指南,-CSDN博客 第二步&#xff1a;选择…...

k8s的pod挂载共享内存

k8s的pod挂载共享内存&#xff0c;限制不生效问题&#xff1a; 注&#xff1a;/dev/shm 是 Linux 系统中用于共享内存的特殊路径。通过将 emptyDir 的 medium 设置为 Memory&#xff0c;可以确保 /dev/shm 正确地挂载到一个基于内存的文件系统&#xff0c;从而实现高效的共享内…...

ubuntu创建虚拟环境安装ultralytics

安装Python和pip&#xff08;如果尚未安装&#xff09;: sudo apt update sudo apt install python3 python3-pip 安装virtualenv: sudo pip3 install virtualenv 创建虚拟环境: sudo virtualenv -p python3 myenv 这里myenv是虚拟环境的名称&#xff0c;-p python3指定使用…...

【掌握 DDL】:SQL 中的数据库与表管理

掌握 DDL&#xff1a;SQL 中的数据库与表管理 掌握 DDL&#xff1a;SQL 中的数据库与表管理数据库 DDL创建数据库查看数据库查看所有数据库查看数据库创建语句 进入数据库删除数据库备份数据库备份恢复 查看数据库连接深入理解数据库创建与删除数据库字符集与校验规则 表 DLL创…...

【Unity中的数学】—— 四元数

一、四元数的定义&#x1f60e; 四元数是一种高阶复数&#xff0c;是一个四维空间的概念&#xff0c;相对于复数的二维空间。它可以表示为 q s i x j y k z q s ix jy kz qsixjykz&#xff0c;其中 s s s、 x x x、 y y y、 z z z 都是实数&#xff0c;并且满足 i …...

Kubernetes 虚拟机安全关机操作流程

不规范关机的危害 Kubernetes集群&#xff08;尤其是基于VirtualBox搭的&#xff09;关机/暂停时&#xff0c;如果不规范操作&#xff0c;会导致&#xff1a; etcd 数据损坏 kubelet 容器状态丢失 PV 挂载紊乱&#xff08;尤其用了 local PV / hostPath&#xff09; 集群启…...

PDF生成模块开发经验分享

在日常的项目开发中&#xff0c;PDF文档的生成是一个常见的需求。无论是用于申报单、审批结果通知书还是其他业务相关的文档输出&#xff0c;一个高效且灵活的PDF生成功能都是不可或缺的。本文将基于我使用Java&#xff08;Spring Boot&#xff09;和iText库开发PDF生成模块的经…...

vscode docker 调试

目录 启动docker&#xff1a; vscode docker 调试 如果已经安装docker并且启动了。 启动docker&#xff1a; docker exec -it nlf /bin/bash vscode docker 调试 按照图中1 2 3 的顺序&#xff0c;进入&#xff0c;可以加载docker进行调试了。...

HTML01:HTML基本结构

HTML基本结构 <html> <head><meta charset"UTF-8"><title>我的第一个网页</title> </head> <body>我的第一个网页 </body> </html><body、</body等成对的标签&#xff0c;分别叫开发标签和闭合标签单独…...

URP - 屏幕图像(_CameraOpaqueTexture)

首先需要在unity中开启屏幕图像开关才可以使用该纹理 同样只有不透明对象才能被渲染到屏幕图像中 若想要该对象不被渲染到屏幕图像中&#xff0c;可以将其Shader的渲染队列改为 "Queue" "Transparent" 如何在Shader中使用_CameraOpaqueTexture&#xf…...

Inno Setup专业打包指南:从基础到高级应用

Inno Setup专业打包指南&#xff1a;从基础到高级应用 Inno Setup是一款免费开源的Windows安装程序制作工具&#xff0c;以其轻量、易用、功能强大而备受开发者青睐。它通过脚本语言定义安装行为&#xff0c;能够创建标准的Windows安装向导&#xff0c;支持文件安装、注册表操…...

如何在Ubuntu上安装NVIDIA显卡驱动?

作者&#xff1a;算力魔方创始人/英特尔创新大使刘力 一&#xff0c;前言 对于使用NVIDIA显卡的Ubuntu用户来说&#xff0c;正确安装显卡驱动是获得最佳图形性能的关键。与Windows系统不同&#xff0c;Linux系统通常不会自动安装专有显卡驱动。本文将详细介绍在Ubuntu系统上安…...

MySQL 主从配置超详细教程

文章目录 前言一、安装 MySQL二、主服务器&#xff08;Master&#xff09;配置三、从服务器&#xff08;Slave&#xff09;配置四、测试主从复制五、注意事项 前言 MySQL 主从配置是一种实用的数据库架构&#xff0c;主服务器处理写入操作&#xff0c;从服务器负责只读操作&am…...