SpringBoot中@Async使用注意事项
前言
@Async这个注解想必大家都用过,是用来实现异步调用的。一个方法加上这个注解以后,当被调用时会使用新的线程来调用。但其实这里面也有一个坑。
问题
这个注解使用时存在如下问题:在没有自定义线程池的场景下,默认会采用SimpleAsyncTaskExecutor创建线程,线程池的最大大小为Integer的MAX_VALUE,相当于调用一次创建一个线程,缺乏线程重用机制。在并发大的场景下可能引发严重性能问题。下面是他的源代码:
/*** {@link TaskExecutor} implementation that fires up a new Thread for each task,* executing it asynchronously.** <p>Supports limiting concurrent threads through the "concurrencyLimit"* bean property. By default, the number of concurrent threads is unlimited.** <p><b>NOTE: This implementation does not reuse threads!</b> Consider a* thread-pooling TaskExecutor implementation instead, in particular for* executing a large number of short-lived tasks.*/
public class SimpleAsyncTaskExecutor extends CustomizableThreadCreatorimplements AsyncListenableTaskExecutor, Serializable {//省略不重要的方法@Overridepublic void execute(Runnable task, long startTimeout) {Assert.notNull(task, "Runnable must not be null");Runnable taskToUse = (this.taskDecorator != null ? this.taskDecorator.decorate(task) : task);if (isThrottleActive() && startTimeout > TIMEOUT_IMMEDIATE) {this.concurrencyThrottle.beforeAccess();doExecute(new ConcurrencyThrottlingRunnable(taskToUse));}else {doExecute(taskToUse);}}/*** 模板方法,用于实际执行任务.* <p>默认实现创建一个新线程并启动它*/protected void doExecute(Runnable task) {//如果threadFactory为空则直接创建线程执行。Thread thread = (this.threadFactory != null ? this.threadFactory.newThread(task) : createThread(task));thread.start();}}
那么如何解决这个问题呢?可以采用下面的方法:
自定义线程池
有如下几种方式可以配置线程池,一种配置默认线程池,让所有@Async自动共享或者配置单独的线程池,使用@Async时指定线程池。
-
使用配置文件中配置默认线程池
-
application.properties参考配置,yml文件同理。
-
# 线程池创建时的初始化线程数,默认为8 spring.task.execution.pool.core-size=1 # 线程池的最大线`在这里插入代码片`程数,默认为int最大值 spring.task.execution.pool.max-size=1 # 用来缓冲执行任务的队列,默认为int最大值 spring.task.execution.pool.queue-capacity=10 # 线程终止前允许保持空闲的时间 spring.task.execution.pool.keep-alive=60s # 是否允许核心线程超时 spring.task.execution.pool.allow-core-thread-timeout=true # 是否等待剩余任务完成后才关闭应用 spring.task.execution.shutdown.await-termination=false # 等待剩余任务完成的最大时间 spring.task.execution.shutdown.await-termination-period= # 线程名的前缀,设置好了之后可以方便我们在日志中查看处理任务所在的线程池 spring.task.execution.thread-name-prefix=asynctask-
-
-
通过实现接口配置默认线程池
-
实现AsyncConfigurer覆盖getAsyncExecutor()方法。注意:这个方法的优先级比配置文件高。
-
@Configuration @EnableAsync public class AsyncConfig implements AsyncConfigurer {public Executor getAsyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(3); //核心线程数executor.setMaxPoolSize(3); //最大线程数executor.setQueueCapacity(1000); //队列大小executor.setKeepAliveSeconds(600); //线程最大空闲时间executor.setThreadNamePrefix("async-Executor-"); //指定用于新创建的线程名称的前缀。executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 拒绝策略(一共四种,此处省略)// 这一步千万不能忘了,否则报错: java.lang.IllegalStateException: ThreadPoolTaskExecutor not initializedexecutor.initialize();return executor;} }
-
-
单独配置线程池,使用@Async指定线程池
-
这种方式可以给每个async的方法指定单独的线程池,但缺点是开发得知道怎么去设置。
-
/** * 独立线程池配置 */ @Configuration public class TaskExecutorConfig {@Beanpublic TaskExecutor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();// 设置核心线程数executor.setCorePoolSize(1);// 设置最大线程数executor.setMaxPoolSize(1);// 设置队列容量executor.setQueueCapacity(20);// 设置线程活跃时间(秒)executor.setKeepAliveSeconds(60);// 设置默认线程名称executor.setThreadNamePrefix("task-");// 设置拒绝策略executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());// 等待所有任务结束后再关闭线程池executor.setWaitForTasksToCompleteOnShutdown(true);return executor;} }public class AsyncService {@Async("taskExecutor")public void task1() throws InterruptedException {TimeUnit.SECONDS.sleep(1L);log.info("task1 complete");}@Async("taskExecutor")public void task2() throws InterruptedException {TimeUnit.SECONDS.sleep(2L);log.info("task2 complete");}@Async("taskExecutor")public void task3() throws InterruptedException {TimeUnit.SECONDS.sleep(3L);log.info("task3 complete");} } -
下面是测试代码,大家可以用这个代码分别测试上述3种方式。
@RestController @RequestMapping("/async") public class AsyncController {@AutowiredAsyncService asyncService;@RequestMapping("/test")public String test() throws InterruptedException {asyncService.task1();asyncService.task2();asyncService.task3();return "success";} }@Service @Slf4j public class AsyncService {@Asyncpublic void task1() throws InterruptedException {TimeUnit.SECONDS.sleep(1L);log.info("task1 complete");}@Asyncpublic void task2() throws InterruptedException {TimeUnit.SECONDS.sleep(2L);log.info("task2 complete");}@Asyncpublic void task3() throws InterruptedException {TimeUnit.SECONDS.sleep(3L);log.info("task3 complete");} }
-
相关文章:
SpringBoot中@Async使用注意事项
前言 Async这个注解想必大家都用过,是用来实现异步调用的。一个方法加上这个注解以后,当被调用时会使用新的线程来调用。但其实这里面也有一个坑。 问题 这个注解使用时存在如下问题:在没有自定义线程池的场景下,默认会采用Sim…...
IEEE 802.11 RTS/CTS/BA/Management
RTS/CTS IEEE 802.11 RTS/CTS即RTS/CTS协议(Request To Send/Clear To Send)即请求发送/清除发送协议是被802.11无线网络协议采用的一种用来减少由隐藏节点问题所造成的冲突的机制。 相当于一种握手协议,主要用来解决"隐藏终端"问题。"隐藏终端"(Hid…...
【风格迁移】对比度保持连贯性损失 CCPL:解决图像局部失真、视频帧间的连贯性和闪烁
对比度保持连贯性损失 CCPL:解决图像局部失真、视频帧间的连贯性和闪烁 提出背景解法:对比度保持连贯性损失(CCPL) 局部一致性假设 对比学习机制 邻域调节策略 互信息最大化对比学习:在无需标签的情况下有效学习区分…...
【C++】贪心算法
贪心算法(Greedy Algorithm)是一种基于贪心策略的算法,它在每一步选择中都采取当前状态下最优的选择,以希望最终得到全局最优解。贪心算法通常适用于满足最优子结构性质的问题,即问题的最优解可以通过其子问题的最优解…...
记一次dockerfile无法构建问题追溯
我有一个dockerfile如下: ENTRYPOINT ["/sbin/tini","-g", "--"] CMD /home/scrapy/start.sh 我原本的用意是先启动tini,再执行下面的cmd命令启动start.sh。 为啥要用tini? 因为我的这个docker…...
React使用 useImperativeHandle 自定义暴露给父组件的实例方法(包括依赖)
关键词 React useImperativeHandle 摘要 useImperativeHandle 是 React 提供的一个自定义 Hook,用于在函数组件中显式地暴露给父组件特定实例的方法。本文将介绍 useImperativeHandle 的基本用法、常见应用场景,以及如何处理其依赖项,以帮…...
yolov5v7v8目标检测增加计数功能--免费源码
在yolo系列中,很多网友都反馈过想要在目标检测的图片上,显示计数功能。其实官方已经实现了这个功能,只不过没有把相关的参数写到图片上。所以微智启软件工作室出一篇教程,教大家如何把计数的参数打印到图片上。 一、yolov5目标检测…...
JPA常见异常 JPA可能抛出的异常
1、EntityNotFoundException(实体不存在异常): 通过 JPA 查找一个不存在的实体。 2、NonUniqueResultException(非唯一结果异常): 查询返回了多个结果,但期望只有一个结果。 3、TransactionRequiredExcep…...
Dockerfile的艺术:构建高效容器镜像的指令详解与实战指南
在容器化技术风靡全球的今天,Dockerfile作为构建 Docker 镜像的蓝图,其编写技巧与理解深度直接影响着应用部署的效率与稳定性。本文将深入剖析Dockerfile中的核心指令,以实战角度为您呈现一份详尽的解读与操作指南,并在文末抛出一…...
软件开发项目管理中各角色职责介绍
项目经理:项目经理在项目全生命周期中扮演着核心统筹与协调者的角色,负责从项目的启动、规划、执行、监控直至收尾的全过程管理。具体职责包括但不限于以下几点: 制定项目计划:依据项目业务主客户需求,明确项目范围、时…...
将时间转换为 `刚刚`、`几秒前`、`几分钟前`、`几小时前`、`几天前`、几月前或按照传入格式显示
const formatPast (date, type "default", zeroFillFlag true) > {// 定义countTime变量,用于存储计算后的数据let countTime;// 获取当前时间戳let time new Date().getTime();// 转换传入参数为时间戳let afferentTime new Date(date).getTime(…...
Oracle存储过程干货(二):PLSQL控制语句
注:本文的数据都来源于,oracle自带的emp表。 —if then elsif end if,单条件判断— declarev_grade char(1); beginv_grade : B;if v_grade A thendbms_output.put_line(哥真牛逼);elsedbms_output.put_line(哥还得加油);end if; end; /—if then els…...
深入Gradle:初识构建自动化的魅力
在软件开发的世界中,构建工具是不可或缺的一部分。它们帮助我们自动化编译、测试和打包应用程序的过程,从而节省时间并减少错误。在众多构建工具中,Gradle以其灵活性、可扩展性和卓越的性能而脱颖而出。本篇文章将带你走进Gradle的世界&#…...
cpp版ros2、opencv转换
ros2转opencv #include <opencv2/opencv.hpp> #include <cv_bridge/cv_bridge.h> #include <sensor_msgs/image_encodings.hpp> subscriber_ this->create_subscription<sensor_msgs::msg::Image>( "img", 10, std::bind(&Subs…...
使用API接口竞品价格监控
步骤一:确定监控目标和KPIs 目标:明确您希望通过监控竞品价格来实现的目标,例如保持价格竞争力、检测价格波动等。KPIs:设定关键绩效指标,如价格变动幅度、价格调整频率等。 步骤二:选择数据源和API 电商…...
Redis的BitMap的使用
简介 Redis的Bitmap不是一个独立的数据结构类型,而是基于字符串(String)类型实现的一种功能 ,存储的是二进制的文件,布隆过滤器就是基于BitMap实现的。 语句的使用 新增操作 setbit key offset value offset的首位…...
视频号带货究竟怎么做?老阳分享的项目怎么样?
在当今社会,随着互联网的快速发展,社交媒体已经成为人们日常生活中不可或缺的一部分。在这个背景下,视频号带货作为一种新兴的电商模式,逐渐崭露头角。许多人都想通过加入视频号带货行业来实现自己的财富自由。其中,老…...
AI智能分析网关V4智慧环保/智慧垃圾站视频智能分析与监控方案
一、背景介绍 随着城市化进程的加速,垃圾处理问题日益受到人们的关注,传统的垃圾站管理方式已经无法满足现代社会的需求。针对当前垃圾站的监管需求,TSINGSEE青犀可基于旗下视频智能检测AI智能分析网关V4与安防监控视频综合管理系统EasyCVR平…...
vxe-table编辑单元格动态插槽slot的使用
业务场景:表格中只有特定某一行的的单元格可以编辑,列很多,为每个列写个插槽要写很多重复代码,所以这里使用动态插槽,简化代码量。显示编辑图标,点击编辑图标隐藏。失去焦点保存调后台接口。 解决办法&…...
2024新鲜出炉阿里巴巴面试真题,如果不想35岁被淘汰这篇文章必看
最近看到群里看到一个女生,讲述了她从开始选择Android,经过非常努力的学习和挣扎,然而最后面对当前的环境却不得不放弃。看完以后真的非常替她感觉惋惜,如果早几年入行可能结果会比现在好很多,但可惜,这就是…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...
Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...
优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...
为什么要创建 Vue 实例
核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...
