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时代已经到来!企业的技术架构,总是伴随着不断增长的数据与日趋复杂的业务持续演进。如何通过构建更易用的技术架构来聚焦在业务本身,而不必在底层基础设施的管理上投入过多的精力,是数据驱动型企业需要思考的重要议题。…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...
YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...
【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...
基于SpringBoot在线拍卖系统的设计和实现
摘 要 随着社会的发展,社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统,主要的模块包括管理员;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...
代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...
