SpringBoot异步方法支持注解@Async应用

news/2025/1/11 7:11:02/

SpringBoot异步方法支持注解@Async应用

1.为什么需要异步方法?

合理使用异步方法可以有效的提高执行效率

同步执行(同在一个线程中):图片

异步执行(开启额外线程来执行):
图片

2.SpringBoot中的异步方法支持

在SpringBoot中并不需要我们自己去创建维护线程或者线程池来异步的执行方法, SpringBoot已经提供了异步方法支持注解.

@EnableAsync // 使用异步方法时需要提前开启(在启动类上或配置类上)
@Async // 被async注解修饰的方法由SpringBoot默认线程池(SimpleAsyncTaskExecutor)执行

service层:

@Service
public class ArticleServiceImpl {// 查询文章public String selectArticle() {//  模拟文章查询操作System.out.println("查询任务线程"+Thread.currentThread().getName());return "文章详情";}// 文章阅读量+1@Asyncpublic void updateReadCount() {try {// 模拟耗时操作Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("更新任务线程"+Thread.currentThread().getName());}
}

controller层:

@RestController
public class AsyncTestController {@Autowiredprivate ArticleServiceImpl articleService;/*** 模拟获取文章后阅读量+1*/@GetMapping("/article")public String getArticle() {long start = System.currentTimeMillis();// 查询文章String article = articleService.selectArticle();// 阅读量+1articleService.updateReadCount();long end = System.currentTimeMillis();System.out.println("文章阅读业务执行完毕,执行共计耗时:"+(end-start));return article;}}

测试结果: 我们可以感受到接口响应速度大大提升, 而且从日志中key看到两个执行任务是在不同的线程中执行的

查询任务线程http-nio-8800-exec-3
文章阅读业务执行完毕,执行共计耗时:56
更新任务线程task-1

3.自定义线程池执行异步方法

SpringBoot为我们默认提供了线程池(SimpleAsyncTaskExecutor)来执行我们的异步方法, 我们也可以自定义自己的线程池.

第一步配置自定义线程池

@EnableAsync // 开启多线程, 项目启动时自动创建
@Configuration
public class AsyncConfig {@Bean("customExecutor")public ThreadPoolTaskExecutor asyncOperationExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();// 设置核心线程数executor.setCorePoolSize(8);// 设置最大线程数executor.setMaxPoolSize(20);// 设置队列大小executor.setQueueCapacity(Integer.MAX_VALUE);// 设置线程活跃时间(秒)executor.setKeepAliveSeconds(60);// 设置线程名前缀+分组名称executor.setThreadNamePrefix("AsyncOperationThread-");executor.setThreadGroupName("AsyncOperationGroup");// 所有任务结束后关闭线程池executor.setWaitForTasksToCompleteOnShutdown(true);// 初始化executor.initialize();return executor;}
}

第二步, 在@Async注解上指定执行的线程池即可

// 文章阅读量+1
@Async("customExecutor")
public void updateReadCount() {// TODO 模拟耗时操作try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("更新文章阅读量线程"+Thread.currentThread().getName());
}

测试结果:

查询任务线程http-nio-8800-exec-1
文章阅读业务执行完毕,执行共计耗时:17
更新任务线程AsyncOperationThread-1

4.如何捕获(无返回值的)异步方法中的异常

以实现AsyncConfigurer接口的getAsyncExecutor方法和getAsyncUncaughtExceptionHandler方法改造配置类

自定义异常处理类CustomAsyncExceptionHandler

@EnableAsync // 开启多线程, 项目启动时自动创建
@Configuration
public class AsyncConfig implements AsyncConfigurer {@Overridepublic Executor getAsyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();// 设置核心线程数executor.setCorePoolSize(8);// 设置最大线程数executor.setMaxPoolSize(20);// 设置队列大小executor.setQueueCapacity(Integer.MAX_VALUE);// 设置线程活跃时间(秒)executor.setKeepAliveSeconds(60);// 设置线程名前缀+分组名称executor.setThreadNamePrefix("AsyncOperationThread-");executor.setThreadGroupName("AsyncOperationGroup");// 所有任务结束后关闭线程池executor.setWaitForTasksToCompleteOnShutdown(true);// 初始化executor.initialize();return executor;}@Overridepublic AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return new CustomAsyncExceptionHandler();}
}
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {@Overridepublic void handleUncaughtException(Throwable throwable, Method method, Object... obj) {System.out.println("异常捕获---------------------------------");System.out.println("Exception message - " + throwable.getMessage());System.out.println("Method name - " + method.getName());for (Object param : obj) {System.out.println("Parameter value - " + param);}System.out.println("异常捕获---------------------------------");}}

测试结果:

查询任务线程http-nio-8800-exec-1
文章阅读业务执行完毕,执行共计耗时:20
异常捕获---------------------------------
Exception message - / by zero
Method name - updateReadCount
异常捕获---------------------------------

5.如何获取(有返回值)异步方法的返回值

使用Future类及其子类来接收异步方法返回值

注意:

  • 无返回值的异步方法抛出异常不会影响Controller的主要业务逻辑
  • 有返回值的异步方法抛出异常会影响Controller的主要业务逻辑
// 异步方法---------------------------------------------------------------------
@Async
public CompletableFuture<Integer> updateReadCountHasResult() {try {// 模拟耗时操作Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("更新文章阅读量线程"+Thread.currentThread().getName());return CompletableFuture.completedFuture(100 + 1);
}// Controller调用---------------------------------------------------------------------
@GetMapping("/article")
public String getArticle() throws ExecutionException, InterruptedException {// 查询文章String article = articleService.selectArticle();// 阅读量+1CompletableFuture<Integer> future = articleService.updateReadCountHasResult();int count = 0;// 循环等待异步请求结果while (true) {if(future.isCancelled()) {System.out.println("异步任务取消");break;}if (future.isDone()) {count = future.get();System.out.println(count);break;}}System.out.println("文章阅读业务执行完毕");return article + count;
}

6.常见失效场景

  1. 主启动类或者配置类没有添加@EnableAsync注解
  2. A方法调用被@Async注解修饰的B方法
@RestController
public class UserController {@Resourceprivate UserService userService;@RequestMapping("/getAll")public void getUsers(){System.out.println("业务开始");test();System.out.println("业务结束");}@Asyncpublic void test(){try {Thread.sleep(2000);System.out.println(Thread.currentThread().getName()+"查询到了所有的用户信息!");} catch (InterruptedException e) {throw new RuntimeException(e);}}
}
  1. 被@Async注解修饰的方法必须不可以是static和private,必须为public
  2. 需要通过@Autowired或@Resource进行注入,不可手动new,基于SpringAOP实现

http://www.ppmy.cn/news/1070590.html

相关文章

Oracle-day3:子查询、with as语句、聚合函数

一、单行子查询 /*一、单行子查询格式&#xff1a;select <列明表> from 表名(查询select 语句)where 列或表达式 比较运算符(SELECT 列名 FROM 表名 WHERE 条件)-- 子查询&#xff0c;必须要使用小括号括起来---最大值函数&#xff1a;max()最小值函数: min()二、 from…

国际腾讯云账号云服务器网络访问丢包问题解决办法!!

本文主要介绍可能引起云服务器网络访问丢包问题的主要原因&#xff0c;及对应排查、解决方法。下面一起了解腾讯云国际云服务器网络访问丢包问题解决办法&#xff1a; 可能原因 引起云服务器网络访问丢包问题的可能原因如下&#xff1a; 1.触发限速导致 TCP 丢包 2.触发限速导致…

机器学习笔记之核函数再回首:Nadarya-Watson核回归python手写示例

机器学习笔记之核函数再回首——Nadaraya-Watson核回归手写示例 引言回顾&#xff1a; Nadaraya-Watson \text{Nadaraya-Watson} Nadaraya-Watson核回归通过核函数描述样本之间的关联关系使用 Softmax \text{Softmax} Softmax函数对权重进行划分将权重与相应标签执行加权运算 N…

微信开发之一键踢出群聊的技术实现

简要描述&#xff1a; 删除群成员 请求URL&#xff1a; http://域名地址/deleteChatRoomMember 请求方式&#xff1a; POST 请求头Headers&#xff1a; Content-Type&#xff1a;application/jsonAuthorization&#xff1a;login接口返回 参数&#xff1a; 参数名必选…

shell部分语法

1 往文件中写入大量内容 cat << EOF >test.sh FROM docker-dev-go1.20.1:heads-v17.03 RUN rm -rf /usr/local/go; mkdir /usr/local/go COPY go /usr/local/go EOF

MVC基础

案列&#xff08;模拟银行转账&#xff09;&#xff1a; 先不采用MVC构建&#xff08;分析缺点&#xff09;&#xff1a;

element plus 设置tree默认选中

<!-- 公共树组件 --> //新建的 tree.vue<template><div style="font-size: 14px; font-weight: 600; height: 50px; border-bottom: 1px solid #eee;line-height: 50px; padding-left: 10px;">{{props.name }}</div><div style="hei…

RISC-V Linux_kernel制作

文章目录 1、下载2、编译 1、下载 Linux 官网地址:https://www.kernel.org $ wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.181.tar.xz $ tar xvf linux-5.10.181.tar.xz $ cd linux-5.10.1812、编译 安装依赖 $ sudo apt-get install -y flex bison bui…