springboot(10)异步任务
文章目录
- 1、SpringBoot异步任务
- 1.1使用注解EnableAsync开启异步任务支持
- 1.2使用`@Async`注解标记要进行异步执行的方法
- 1.3controller测试
- 2.异步任务相关限制
- 3.1自定义 Executor
- 3.1.1应用层级:
- 3.1.2方法层级:
- 3.2自定义 Executor (第二种方式)
- 4.1异常处理
- 4.1.1自定义一个异常处理器类实现接口如下所示:
- 4.1.2创建一个自定义`Executor`异步配置类,将我们的自定义异常处理器设置到其接口上
- 4.1.3测试
- 4.2异常处理(第二种方式)
在SpringBoot应用程序中,有时需要执行一些长时间运行的操作,如发送电子邮件或从外部API获取数据。 这些操作可能需要几秒钟或几分钟才能完成。 如果您在主线程上执行此类操作,则应用程序停止响应,可能会导致用户体验不佳。
为了避免这种情况,并使应用程序在执行此类操作时继续响应,我们可以使用SpringBoot的异步任务功能。 异步任务是指可以在后台线程上执行的任务,因此不会阻塞主线程。
有时候,前端可能提交了一个耗时任务,如果后端接收到请求后,直接执行该耗时任务,那么前端需要等待很久一段时间才能接受到响应。如果该耗时任务是通过浏览器直接进行请求,那么浏览器页面会一直处于转圈等待状态。
事实上,当后端要处理一个耗时任务时,通常都会将耗时任务提交到一个异步任务中进行执行,此时前端提交耗时任务后,就可直接返回,进行其他操作。
1、SpringBoot异步任务
SpringBoot开启异步任务的步骤如下:
- 在SpringBootApplication类上添加@EnableAsync注解,启用SpringBoot异步任务支持。
- 在异步方法所在的类上添加@Async注解,标记该类中需要异步执行的方法。
- 在异步方法中使用CompletableFuture等类处理异步操作。
使用SpringBoot异步任务功能可以轻松地将长时间运行的操作转换为异步任务,提高应用程序的响应性能和用户体验。
1.1使用注解EnableAsync开启异步任务支持
@SpringBootApplication
@EnableAsync
public class ApplicationStarter {public static void main(String[] args) {SpringApplication.run(ApplicationStarter.class,args);}
}
1.2使用@Async注解标记要进行异步执行的方法
@Service
public class AsyncService {@Asyncpublic void t1() throws InterruptedException {// 模拟耗时任务Thread.sleep(TimeUnit.SECONDS.toMillis(5));}@Asyncpublic Future<String> t2() throws InterruptedException {// 模拟耗时任务Thread.sleep(TimeUnit.SECONDS.toMillis(5));return new AsyncResult<>("async tasks done!");}
}
1.3controller测试
@Autowiredprivate AsyncService asyncService;@GetMapping("/task1")public String asyncTaskWithoutReturnType() throws InterruptedException {asyncService.t1();return "rrrr";}@GetMapping("/task2")public String asyncTaskWithReturnType() throws InterruptedException, ExecutionException {asyncService.t2();return "aaaaaaa";}
被@Async注解的方法可以接受任意类型参数,但只能返回void或Future类型数据
所以当异步方法返回数据时,需要使用Future包装异步任务结果,上述代码使用AsyncResult包装异步任务结果,AsyncResult间接继承Future,是 Spring 提供的一个可用于追踪异步方法执行结果的包装类。其他常用的Future类型还有 Spring 4.2 提供的ListenableFuture,或者 JDK 8 提供的CompletableFuture,这些类型可提供更丰富的异步任务操作。
如果前端需要获取耗时任务结果,则异步任务方法应当返回一个Future类型数据,此时Controller相关接口需要调用该Future的get()方法获取异步任务结果,get()方法是一个阻塞方法,因此该操作相当于将异步任务转换为同步任务,浏览器同样会面临我们前面所讲的转圈等待过程,但是异步执行还是有他的好处的,因为我们可以控制get()方法的调用时序,因此可以先执行其他一些操作后,最后再调用get()方法。
2.异步任务相关限制
被@Async注解的异步任务方法存在相关限制:
- 被
@Async注解的方法必须是public的,这样方法才可以被代理。 - 不能在同一个类中调用
@Async方法,因为同一个类中调用会绕过方法代理,调用的是实际的方法。 - 被
@Async注解的方法不能是static。 @Async注解不能与 Bean 对象的生命周期回调函数(比如@PostConstruct)一起注解到同一个方法中。- 异步类必须注入到 Spring IOC 容器中(也即异步类必须被
@Component/@Service等进行注解)。 - 其他类中使用异步类对象必须通过
@Autowired等方式进行注入,不能手动new对象。
SpringBoot异步任务功能虽然可以提高应用程序的响应性能,但还是有一些限制需要注意:
- 异步方法不能与同步方法在同一个类中定义。必须将异步方法定义在单独的类中,或者使用CGLIB代理来将异步方法包装在另一个类中。
- 异步方法不能是private或final的。
- 异步方法不能返回void类型。必须返回一个Future或CompletableFuture对象,以便在异步任务完成后处理结果。
- 如果异步方法在同一类中调用另一个异步方法,则调用将不会异步执行,而是在同一线程中同步执行。
总之,虽然SpringBoot的异步任务功能可以提高应用程序的响应性能,但在使用时需要注意这些限制,以确保异步方法能够正确地执行并保持应用程序的稳定性。
3.1自定义 Executor
默认情况下,Spring 会自动搜索相关线程池定义:要么是一个唯一TaskExecutor Bean 实例,要么是一个名称为taskExecutor的Executor Bean 实例。如果这两个 Bean 实例都不存在,就会使用SimpleAsyncTaskExecutor来异步执行被@Async注解的方法。
综上,可以知道,默认情况下,Spring 使用的 Executor 是SimpleAsyncTaskExecutor,SimpleAsyncTaskExecutor每次调用都会创建一个新的线程,不会重用之前的线程。很多时候,这种实现方式不符合我们的业务场景,因此通常我们都会自定义一个 Executor 来替换SimpleAsyncTaskExecutor。
对于自定义 Executor(自定义线程池),可以分为如下两个层级:
- 应用层级:即全局生效的 Executor。依据 Spring 默认搜索机制,其实就是配置一个全局唯一的
TaskExecutor实例或者一个名称为taskExecutor的Executor实例即可,如下所示: - 方法层级:即为单独一个或多个方法指定运行线程池,其他未指定的异步方法运行在默认线程池。如下所示:
3.1.1应用层级:
下面代码定义了一个名称为taskExecutor的Executor,此时@Async方法默认就会运行在该Executor中。
@Configuration
public class ExcuterConfig {@Bean("taskExecutor")public Executor getAsyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();// 设置核心线程数int cores = Runtime.getRuntime().availableProcessors();executor.setCorePoolSize(cores);// 设置最大线程数executor.setMaxPoolSize(20);// 等待所有任务结束后再关闭线程池executor.setWaitForTasksToCompleteOnShutdown(true);// 设置线程默认前缀名executor.setThreadNamePrefix("Application-Level-Async-");return executor;}
}
3.1.2方法层级:
package com.buba.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;@Configuration
public class ExcuterConfig {@Bean("methodLevelExecutor1")public TaskExecutor getAsyncExecutor1() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();// 设置核心线程数executor.setCorePoolSize(4);// 设置最大线程数executor.setMaxPoolSize(20);// 等待所有任务结束后再关闭线程池executor.setWaitForTasksToCompleteOnShutdown(true);// 设置线程默认前缀名executor.setThreadNamePrefix("Method-Level-Async1-");return executor;}@Bean("methodLevelExecutor2")public TaskExecutor getAsyncExecutor2() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();// 设置核心线程数executor.setCorePoolSize(8);// 设置最大线程数executor.setMaxPoolSize(20);// 等待所有任务结束后再关闭线程池executor.setWaitForTasksToCompleteOnShutdown(true);// 设置线程默认前缀名executor.setThreadNamePrefix("Method-Level-Async2-");return executor;}
}
上述特意设置了多个TaskExecutor,因为如果只设置一个TaskExecutor,那么 Spring 就会默认采用该TaskExecutor作为所有@Async的Executor,而设置了多个TaskExecutor,Spring 检测到全局存在多个Executor,就会降级使用默认的SimpleAsyncTaskExecutor,此时我们就可以为@Async方法配置执行线程池,其他未配置的@Async就会默认运行在SimpleAsyncTaskExecutor中,这就是方法层级的自定义 Executor。如下代码所示:
@Service
public class AsyncService {@Async("methodLevelExecutor1")public void t1() throws InterruptedException {// 模拟耗时任务Thread.sleep(TimeUnit.SECONDS.toMillis(5));}@Async("methodLevelExecutor2")public Future<String> t2() throws InterruptedException {// 模拟耗时任务Thread.sleep(TimeUnit.SECONDS.toMillis(5));return new AsyncResult<>("async tasks done!");}
}
3.2自定义 Executor (第二种方式)
Spring Boot提供了默认的任务执行器,但是有时候我们需要自定义任务执行器以更好地控制任务执行的线程池。在Spring Boot中,我们可以通过实现AsyncConfigurer接口来自定义任务执行器。下面是一个使用自定义任务执行器的示例:
@Configuration
@EnableAsync
public class AppConfig implements AsyncConfigurer {@Overridepublic Executor getAsyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(10);executor.setMaxPoolSize(100);executor.setQueueCapacity(500);executor.setThreadNamePrefix("MyExecutor-");executor.initialize();return executor;}
}
在上面的示例中,我们实现了AsyncConfigurer接口,并覆盖了getAsyncExecutor()方法。在该方法中,我们创建了一个ThreadPoolTaskExecutor对象,并设置了线程池的核心线程数、最大线程数、队列容量和线程名称前缀。最后,我们返回这个线程池对象。
通过实现AsyncConfigurer接口并覆盖getAsyncExecutor()方法,我们可以轻松地自定义任务执行器。在应用程序中使用自定义任务执行器时,只需将其添加到异步方法所在的类上即可。例如:
@Service
public class MyService {@Async("myExecutor")public CompletableFuture<String> longRunningMethod() {// long running code here}
}
在上面的示例中,我们将@Async注解的value属性设置为"myExecutor",这是我们在AppConfig类中定义的自定义任务执行器的名称。
总之,通过自定义任务执行器,我们可以更好地控制异步任务的执行,从而提高应用程序的性能和可靠性。
4.1异常处理
前文介绍过,对于被@Async注解的异步方法,只能返回void或者Future类型。对于返回Future类型数据,如果异步任务方法抛出异常,则很容易进行处理,因为Future.get()会重新抛出该异常,我们只需对其进行捕获即可。但是对于返回void的异步任务方法,异常不会传播到被调用者线程,因此我们需要自定义一个额外的异步任务异常处理器,捕获异步任务方法抛出的异常。
4.1.1自定义一个异常处理器类实现接口如下所示:
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {@Overridepublic void handleUncaughtException(Throwable throwable, Method method, Object... objects) {System.out.println("Exception message - " + throwable.getMessage());System.out.println("Method name - " + method.getName());for (Object param : objects) {System.out.println("Parameter value - " + param);}}
}
4.1.2创建一个自定义Executor异步配置类,将我们的自定义异常处理器设置到其接口上
@Configuration
@EnableAsync
public class AsyncConfigure implements AsyncConfigurer {@Overridepublic AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return new CustomAsyncExceptionHandler();}
}
此时异步方法如果抛出异常,就可以被我们的自定义异步异常处理器捕获得到。
4.1.3测试
@Service
public class AsyncService {@Async("methodLevelExecutor1")public void t1() throws InterruptedException {// 模拟耗时任务Thread.sleep(TimeUnit.SECONDS.toMillis(5));throw new NullPointerException();}
4.2异常处理(第二种方式)
SpringBoot异步任务的异常可以通过@Async注解的exceptional属性捕获。将exceptional属性设置为要捕获的异常类型即可。例如,以下示例演示如何捕获RuntimeException:
@Async(exceptional = RuntimeException.class)
public CompletableFuture<String> sendEmail() {// send email code here that may throw a RuntimeException
}
在上面的示例中,如果sendEmail()方法抛出RuntimeException,则该异常将被捕获并包装在CompletableFuture对象中返回。
另一种捕获异步任务异常的方法是使用@Async注解的异步方法的返回类型。如果异步方法的返回类型是CompletableFuture,则可以使用CompletableFuture的exceptionally()方法捕获异步任务的异常。例如,以下示例演示如何使用CompletableFuture的exceptionally()方法捕获异步任务的异常:
@Async
public CompletableFuture<String> sendEmail() {// send email code here that may throw a RuntimeException
}CompletableFuture<String> future = sendEmail();
future.exceptionally(ex -> {// handle the exception herereturn "Error sending email: " + ex.getMessage();
});
在上面的示例中,如果sendEmail()方法抛出RuntimeException,则可以在future.exceptionally()方法中捕获该异常并处理它。注意,exceptionally()方法返回一个新的CompletableFuture对象,该对象包含捕获到的异常或原始异步操作的结果。
总之,SpringBoot提供了多种捕获异步任务异常的方法,包括使用@Async注解的exceptional属性和CompletableFuture对象的exceptionally()方法。这些方法可以帮助我们更好地管理和处理异步任务的异常,提高应用程序的可靠性和稳定性。
相关文章:
springboot(10)异步任务
文章目录1、SpringBoot异步任务1.1使用注解EnableAsync开启异步任务支持1.2使用Async注解标记要进行异步执行的方法1.3controller测试2.异步任务相关限制3.1自定义 Executor3.1.1应用层级:3.1.2方法层级:3.2自定义 Executor (第二种方式)4.1异常处理4.1.…...
清华大学开源的chatGLM-6B部署实战
Windows部署 win10 通过wsl部署 常见问题: torch.cuda.OutOfMemoryError: CUDA out of memory. 在Windows的系统环境变量中增加 变量名:PYTORCH_CUDA_ALLOC_CONF 变量值:max_split_size_mb:32 文档书写时使用3090 24G显存配置,其他规格酌情调整 32 至其他值,如未设置变…...
通过矩阵从整体角度搞懂快速傅里叶变换原理
离散傅里叶变换公式 公式 f[k]∑n0N−1g[n]e−i(2π/N)kn,其中(0<n<N)f[k]\sum_{n0}^{N-1}g[n]e^{-i(2\pi/N)kn}, 其中(0<n<N) f[k]n0∑N−1g[n]e−i(2π/N)kn,其中(0<n<N) 逆变换公式 g[n]1N∑k0N−1f[k]ei(2π/N)kn,其中(0<k<N)g[n]\frac{1}{N}\…...
【C++从0到1】25、C++中嵌套使用循环
C从0到1全系列教程 1、实例代码 #include <iostream> // 包含头文件。 using namespace std; // 指定缺省的命名空间。int main() {// 超女分4个小组,每个小组有3名超女,在控制台显示每个超女的小组编号和组内编号。// 用一个循环…...
FastDFS与Nginx结合搭建文件服务器,并内网穿透实现公网访问
文章目录前言1. 本地搭建FastDFS文件系统1.1 环境安装1.2 安装libfastcommon1.3 安装FastDFS1.4 配置Tracker1.5 配置Storage1.6 测试上传下载1.7 与Nginx整合1.8 安装Nginx1.9 配置Nginx2. 局域网测试访问FastDFS3. 安装cpolar内网穿透4. 配置公网访问地址5. 固定公网地址5.1 …...
密集场景下的行人跟踪替代算法,头部跟踪算法 | CVPR 2021
一个不知名大学生,江湖人称菜狗 original author: Jacky LiEmail : 3435673055qq.com Time of completion:2023.4.8 Last edited: 2023.4.8 目录 摘要 主要内容 结果 这篇文章是CVPR 2021 的最新论文,文章的标题: 文章的主要内…...
Matlab与ROS(1/2)---服务端和客户端数据通信(五)
0. 简介 在前几讲我们讲了Matlab中的Message以及Topic的相关知识。而ROS主要支持的通信机制还有服务这一类。服务通过允许请求以及响应的通信方式,来给整个系统完成更紧密的耦合。服务客户端向服务服务器发送请求消息并等待响应。服务器将使用请求中的数据构造响应…...
数字化转型的避坑指南:细说数字化转型十二大坑
随着信息技术的快速发展,数字化转型已经成为许多企业发展的必经之路。然而,数字化转型过程中也存在许多坑,如果不谨慎处理,就可能导致企业陷入困境。本文将细说数字化转型的十二大坑,并提供相应的避坑指南。 1、不了解…...
pt05Encapsulationinherit
Encapsulation &inherit 封装继承 封装 向类外提供必要的功能,隐藏实现的细节, 代码可读性更高优势:简化编程,使用者不必了解具体的实现细节,只需要调用对外提供的功能。私有成员:作用:无需向类外提供…...
面向对象编程(基础)9:封装性(encapsulation)
目录 9.1 为什么需要封装? 而“高内聚,低耦合”的体现之一: 9.2 何为封装性? 9.3 Java如何实现数据封装 9.4 封装性的体现 9.4.1 成员变量/属性私有化 实现步骤: 成员变量封装的好处: 9.4.2 私有化…...
fate-serving-server增加取数逻辑并源码编译
1.什么是fate-serving-server? FATE-Serving 是一个高性能、工业化的联邦学习模型服务系统,专为生产环境而设计,主要用于在线推理。 2.fate-serving-server源码编译 下载fate-serving-serving项目(GitHub - FederatedAI/FATE-Serving: A scalable, h…...
循环队列、双端队列 C和C++
队列 目录 概念 实现方式 顺序队列 循环队列 队列的数组实现 用循环链表实现队列 STL 之 queue 实现队列 STL 之 dequeue 实现双端队列 概念 队列是一种特殊的线性表,它只允许在表的前端(称为队头,front)进行删除操作…...
正则表达式(语法+例子)
文章目录一、介绍二、语法1、匹配字符2、表示数量的字符3、边界字符4、其他字符5、转义字符三、例子1、邮箱2、用逗号分隔的数字集合1,23、允许一位小数4、20yy-mm-dd日期格式5、手机号6、匹配html、xml标签一、介绍 正则表达式(Regular Expression)&am…...
Properties和IO流集合的方法
方法名说明void load(InputStream inStream)从输入字节流读取属性列表(键和元素)void load(Reader reader)从输入字符流读取属性列表(键和元素对)void store(OutputStream out,String comments)将此属性列表(键和元素对…...
python 生成器、迭代器、动态新增属性及方法
目录 一、生成器 1、生成器定义 2、生成器存在的意义 3、创建生成器方式一(生成器表达式) 4. 创建生成器方式二(生成器函数) 1. 生成器函数 2. 生成器函数的工作原理 5. 总结 1. 什么是生成器 2. 生成器特点 二、迭代器…...
Java处理JSON
Java处理json有很多种方法,在这里总结一下。 1 Jackson Spring MVC 默认采用Jackson解析Json,出于最小依赖的考虑,也许Json解析第一选择就应该是Jackson。 1.1 引入的包 Jackson核心模块由三部分组成:jackson-core、jackson-a…...
58-Map和Set练习-LeetCode692前k个高频单词
题目 给定一个单词列表 words 和一个整数 k ,返回前 k 个出现次数最多的单词。 返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率, 按字典顺序 排序。 示例 1: 输入: words ["i", "love", …...
线程生命周期及五种状态
文章目录一、线程生命周期及五种状态1、New(初始化状态)2、Runnable(就绪状态)3、Running(运行状态)4、Blocked(阻塞状态)5、Terminated(终止状态)二、线程基本方法1、线程等待(wait)2、线程睡眠(sleep)3、…...
OBCP第八章 OB运维、监控与异常处理-灾难恢复
灾难恢复是指当数据库中的数据在被有意或无意破坏后复原数据库所需要执行的活动 回收站:回收站在原理上说就是一个数据字典表,放置用户删除的数据库对象信息。用户删除的东西被放入回收站后,其实仍然占据着物理空间,除非您手动进…...
亚马逊云科技Serverless Data:数字经济下的创新动能
Serverless时代已经到来!企业的技术架构,总是伴随着不断增长的数据与日趋复杂的业务持续演进。如何通过构建更易用的技术架构来聚焦在业务本身,而不必在底层基础设施的管理上投入过多的精力,是数据驱动型企业需要思考的重要议题。…...
docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...
【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...
Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...
Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...
MySQL 主从同步异常处理
阅读原文:https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主,遇到的这个错误: Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一,通常表示ÿ…...
SpringAI实战:ChatModel智能对话全解
一、引言:Spring AI 与 Chat Model 的核心价值 🚀 在 Java 生态中集成大模型能力,Spring AI 提供了高效的解决方案 🤖。其中 Chat Model 作为核心交互组件,通过标准化接口简化了与大语言模型(LLM࿰…...
