SpringBoot中@EnableAsync和@Async注解的使用

news2024/10/5 15:34:14

目录

    • 1.@EnableAsync 注解
      • 1.1 配置类使用示例
      • 1.2 复制请求上下文
    • 2.用法1:@Async 注解
      • 2.1 测试Controller
      • 2.2 测试Service
      • 2.3 测试ServiceImpl
      • 2.4.测试
    • 4.用法2:直接使用 taskExecutor 做异步
      • 4.1 重新实现:测试ServiceImpl
      • 4.2 测试
    • 5.@Async异步不生效原因

在 Spring Boot 中,可以通过 @EnableAsync 注解来启动异步方法调用的支持,通过 @Async 注解来标识异步方法,让方法能够在异步线程中执行。下面分别介绍它们的使用方法。

1.@EnableAsync 注解

@EnableAsync 是一个 Spring Boot 中用于启动异步方法调用的注解。使用 @EnableAsync 注解时,需要将其放置在一个配置类上,并且在配置类中通过 @Bean 方法创建一个线程池。

下面举个例子:

1.1 配置类使用示例

AsyncTaskExecutorConfig 类通过 @EnableAsync 注解来启用异步方法调用,然后在配置类中通过 @Bean 方法创建了一个名为 asyncExecutor 的线程池,用于执行异步方法。

import com.demo.async.ContextCopyingDecorator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;

/**
 * <p> @Title AsyncTaskExecutorConfig
 * <p> @Description 异步线程池配置
 *
 * @author ACGkaka
 * @date 2023/4/24 19:48
 */
@EnableAsync
@Configuration
public class AsyncTaskExecutorConfig {

    /**
     * 核心线程数(线程池维护线程的最小数量)
     */
    private int corePoolSize = 10;
    /**
     * 最大线程数(线程池维护线程的最大数量)
     */
    private int maxPoolSize = 200;
    /**
     * 队列最大长度
     */
    private int queueCapacity = 10;

    @Bean
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setThreadNamePrefix("MyExecutor-");
        // for passing in request scope context 转换请求范围的上下文
        executor.setTaskDecorator(new ContextCopyingDecorator());
        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.initialize();
        return executor;
    }
}

1.2 复制请求上下文

ContextCopyingDecorator 类使用了装饰者模式,用于将主线程中的请求上下文拷贝到异步子线程中,并且在异步子线程执行之后清空请求的上下文。

import org.slf4j.MDC;
import org.springframework.core.task.TaskDecorator;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.util.Map;

/**
 * <p> @Title ContextCopyingDecorator
 * <p> @Description 上下文拷贝装饰者模式
 *
 * @author ACGkaka
 * @date 2023/4/24 20:20
 */
public class ContextCopyingDecorator implements TaskDecorator {
    @Override
    public Runnable decorate(Runnable runnable) {
        try {
            // 从父线程中获取上下文,然后应用到子线程中
            RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
            Map<String, String> previous = MDC.getCopyOfContextMap();
            SecurityContext securityContext = SecurityContextHolder.getContext();
            return () -> {
                try {
                    if (previous == null) {
                        MDC.clear();
                    } else {
                        MDC.setContextMap(previous);
                    }
                    RequestContextHolder.setRequestAttributes(requestAttributes);
                    SecurityContextHolder.setContext(securityContext);
                    runnable.run();
                } finally {
                    // 清除请求数据
                    MDC.clear();
                    RequestContextHolder.resetRequestAttributes();
                    SecurityContextHolder.clearContext();
                }
            };
        } catch (IllegalStateException e) {
            return runnable;
        }
    }
}

2.用法1:@Async 注解

@Async 注解是一个 Spring Boot 中用于标识异步方法的注解,通过在方法上添加 @Async 注解,可以让该方法在异步线程中执行。

下面举个例子:

2.1 测试Controller

DemoController 类中声明了 /demo/test 接口,接口中调用了 demoService.testError() 方法。

import com.demo.common.Result;
import com.demo.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * <p> @Title DemoController
 * <p> @Description 测试Controller
 *
 * @author ACGkaka
 * @date 2023/4/24 18:02
 */
@RestController
@RequestMapping("/demo")
public class DemoController {

    @Autowired
    private DemoService demoService;

    @RequestMapping("/test")
    public Result<Object> test() {
        Result<Object> result = Result.succeed();
        System.out.println("start...");
        demoService.testError();
        System.out.println("end...");
        return result;
    }
}

2.2 测试Service

/**
 * <p> @Title DemoService
 * <p> @Description 测试Service
 *
 * @author ACGkaka
 * @date 2023/4/24 18:13
 */
public interface DemoService {

    /**
     * 测试异常
     */
    void testError() throws InterruptedException;
}

2.3 测试ServiceImpl

DemoServiceImpl 类使用了 @Async 注解,用于异步调用,testError() 方法中抛出了异常,用于测试异步执行。

这里 @Async 注解的 value 值指定了我们在配置类中声明的 taskExecutor 线程池。

  • 假如只配置了一个线程池,直接用 @Async 注解就会用自定义的线程池执行。
  • 假如配置了多个线程池,用 @Async("name") 来指定使用哪个线程池,如果没有指定,会用默认的 SimpleAsyncTaskExecutor 来处理。
import com.demo.service.DemoService;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

/**
 * <p> @Title DemoServiceImpl
 * <p> @Description 测试ServiceImpl
 *
 * @author ACGkaka
 * @date 2023/4/24 18:14
 */
@Service
public class DemoServiceImpl implements DemoService {
    @Async("taskExecutor")
    @Override
    public void testError() throws InterruptedException {
        throw new RuntimeException("测试异常");
    }
}

2.4.测试

访问接口:http://localhost:8080/demo/test

访问结果如下,可见异常并没有接口返回正常的结果,异步测试成功。

在这里插入图片描述

4.用法2:直接使用 taskExecutor 做异步

由于我们在第1步中,将异步线程池注入到了 taskExecutor Bean 容器中,我们就可以直接通过 @Autowired 或者 @Resource 获取到线程池,然后使用。

我们通过直接使用 taskExecutor 线程池的方式,重新实现 DemoServiceImpl.java:

4.1 重新实现:测试ServiceImpl

import com.demo.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Service;

/**
 * <p> @Title DemoServiceImpl
 * <p> @Description 测试ServiceImpl
 *
 * @author ACGkaka
 * @date 2023/4/24 18:14
 */
@Service
public class DemoServiceImpl implements DemoService {

    @Qualifier("taskExecutor")
    @Autowired
    private TaskExecutor taskExecutor;

    @Override
    public void testError() {
        taskExecutor.execute(() -> {
            throw new RuntimeException("测试异常");
        });
    }
}

4.2 测试

访问接口:http://localhost:8080/demo/test

访问结果如下,可见异常并没有接口返回正常的结果,异步测试成功,直接使用线程池的方式也可行。

在这里插入图片描述

在这里插入图片描述

5.@Async异步不生效原因

1)@SpringBootApplication 启动类或配置类当中没有添加 @EnableAsync 注解。

(补充:项目中除了启动类和配置类外,任何一个注入到 Bean 容器中的类添加 @EnableAsync 注解都可以,但是规范用法是在启动类和配置类中添加注解。)

2)异步方法使用注解@Async的返回值只能为void或者Future。

3)异步方法不能使用static修饰

4)异步类没有使用 @Component 注解(或其他注解)导致spring无法扫描到异步类

5)异步方法不能与异步方法在同一个类中

6)类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象

7)在 @Async 方法上标注 @Transactional 是没用的。 在 @Async 方法调用的方法上标注@Transactional 有效。

8)调用被 @Async 标记的方法的调用者不能和被调用的方法在同一类中不然不会起作用!!!!!!!

9)使用 @Async 时是获取不到方法的返回值的,拿到的值为 null,如果返回的值是原始类型int、double、long等(不能为 null),就会报错。

在这里插入图片描述

整理完毕,完结撒花~ 🌻





参考地址:

1.@EnableAsync@Async基本使用方法,https://www.cnblogs.com/fzhblog/p/14012401.html

2.spring boot- @EnableAsync和@Async(Spring boot 注解@Async不生效 无效 不起作用),https://blog.csdn.net/inthat/article/details/111162059

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/458022.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

保护模式段描述符

目前为止&#xff0c;内存还是分段模式&#xff0c;所以想要保护内存&#xff0c;就需要保存段。由于CPU的扩展导致了32位的段基地址和段内偏移&#xff0c;所以16位的段寄存器就无法放下这些信息。现在就需要把这些信息放到内存中&#xff0c;这些信息被封装成特定的段描述符。…

Vue进阶-Vue cli项目搭建、项目基本操作、axios的使用、路由、Vuex

Vue进阶 Vue cli 一、Vue cli 概述 CLI 全称是 Commond Line Interface&#xff0c;翻译为命令行界面&#xff0c;俗称脚手架。VueCLI是一个官方发布vue.js项目脚手架。用VueCLI 可快速搭建Vue开发环境以及对应webpack配置。 二、环境搭建 1、下载 node.js 下载地址&…

4 redis高可用

所谓的高可用&#xff0c;也叫HA&#xff08;High Availability&#xff09;&#xff0c;是分布式系统架构设计中必须考虑的因素之一&#xff0c;它通常是指&#xff0c;通过设计减少系统不能提供服务的时间。如果在实际生产中&#xff0c;如果redis只部署一个节点&#xff0c;…

Compiler- 自增运算

我们来看一下C语言中的前自增&#xff08;i&#xff09;和后自增(i) 这个经典案例。大家在学习C的时候肯定学过前自增是先自增&#xff0c;然后将结果用于计算&#xff1b;后自增是先参与计算&#xff0c;再增加。 好&#xff0c;看一下这段代码的结果&#xff1a; #include …

PE文件反编译为python脚本流程

1、查壳 DetectltEasy、PeiD查壳 2、脱壳 常见打包工具PyInstaller&#xff0c;脱壳方法 &#xff08;1&#xff09;用pyinstxtractor.py脱壳&#xff0c;用”python pyinstxtractor.py 1.exe“命令&#xff0c;生成“.exe文件名_extracted” &#xff08;2&#xff09;用…

Python+Qt人脸识别门禁管理系统

程序示例精选 PythonQt人脸识别门禁管理系统 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对<<PythonQt人脸识别门禁管理系统>>编写代码&#xff0c;代码整洁&#xff0c;规则&am…

1 ROS2介绍与安装

1 ROS2介绍与安装 1.1 Ubuntu配置与ROS2安装1.1.1 Ubuntu22.04安装1.1.2 下载安装ROS21.1.3 配置ROS2环境并测试 1.2 使用VSCode搭建ROS2开发环境1.2.1 安装并配置VSCode1.2.2 创建ROS2工程的方法1.2.3 使用VSCode创建ROS2的C/C项目1.2.4 使用VSCode创建ROS2的Python项目 1.3 R…

Samba配置回收站功能

部门确实需要给Samba配置回收站&#xff0c;查阅了下回收站的资料&#xff0c;配置也挺简单的。 配置说明&#xff1a; 在Samba配置回收站功能中各参数作用如下。 (1) vfs object recycle&#xff1a;载入Samba用于回收站功能的模块recycle.so。 (2) recycle:repository /Pr…

企业如何保护外发文件的数据安全?

随着数字化转变&#xff0c;企业的业务文件大多通过电子形式在内外部流转。这增加了外发文件数据泄露或被篡改的风险&#xff0c;如何保护外发文件安全已成为企业不容忽视的课题。 企业外发文件&#xff0c;特别是电子文件&#xff0c;存在一定的数据安全风险&#xff1a; 文件…

第十二章 外观模式

文章目录 前言一、外观模式基本介绍完整代码DVD类爆米花类投影仪类屏幕类立体声类灯光类家庭影院类进行聚合Client测试类 二、 外观模式在MyBatis框架应用的源码分析三、外观模式的注意事项和细节 前言 一、外观模式基本介绍 完整代码 DVD类 package tanchishell.SJMS.faca…

Layui 2.8.0 正式发布,朴实归来

Layui 是一套开源的 Web UI 组件库&#xff0c;采用自身轻量级模块化规范&#xff0c;遵循原生态的 HTML/CSS/JavaScript 开发模式&#xff0c;极易上手&#xff0c;拿来即用。其风格简约轻盈&#xff0c;而内在雅致丰盈&#xff0c;甚至包括文档在内的每一处细节都经过精心雕琢…

On the Efficacy of Knowledge Distillation 解析

paper&#xff1a;On the Efficacy of Knowledge Distillation 本文的题目是《论知识蒸馏的有效性》&#xff0c;主要是对教师模型并不是越大越好这一现象进行研究&#xff0c;并提出了缓解方法&#xff1a;early stop。 Bigger models are not better teachers 知识蒸馏背…

S32k3系列开发学习(FlexCAN)

前言 由于之前没有接触过CAN总线模块&#xff0c;对这一块的知识仍比较陌生&#xff0c;于是乎想简单梳理一下CAN总线的工作流程&#xff0c;加深理解。 一、CAN是什么&#xff1f; 参考&#xff1a;https://zhuanlan.zhihu.com/p/346696648 二、CAN框架 各模块功能如下&am…

NLP基础:标注器Label Studio的入门使用

目录 一、环境准备 二、操作 文章来源&#xff1a; 简介&#xff1a; Label Studio是一个开源的数据标注工具&#xff0c;它可以用于各种机器学习和深度学习项目。它的主要目的是帮助数据科学家和机器学习工程师快速、高效地标注数据&#xff0c;以构建和训练准确的机器学…

Linux入门---开发的Linux命令手册

Linux 基础知识 基础 启动过程&#xff1a; 内核的引导。运行 init。系统初始化。建立终端 。用户登录系统。 命令介绍 磁盘 #文件#a&#xff1a;相当於 -pdr 的意思&#xff0c;至於 pdr 请参考下列说明&#xff1b;(常用)&#xff1b;f&#xff1a;为强制(force)进行&…

Ubuntu20.04使用多卡训练HyperNetwork模型和LoRA模型全流程及疑难问题解决方案

目录 一. LoRA模型多卡训练1.1 安装xformer等库1.2 设置路径1.3 多卡训练 二. LoRA模型多卡训练疑难报错解决方案多卡训练报错 软硬件配置&#xff1a; CPU: AMD 5800 8core 16Thread GPU: NVIDIA RTX 3090 *1 NVIDIA TITAN RTX *1 OS: Ubuntu20.04 一. LoRA模型多卡训练 1.1 …

JavaScript概述四(DOM文档对象模型)

1.DOM(Document Object Model) 会把网页里面的元素当成对象去操作,包含对象的属性,属性值,方便我们去 操作网页。 整个页面最终会形成一个对象 :document ,页面里面的所有的元素(如 标签 ) 最终都会转换成 js 里面的对象。 1.1 获取页面的元素&#xff08;通过选择器&#xff0…

S32K3系列单片机学习

前言 定时器中断&#xff0c;每个平台的实现方式均有差异&#xff0c;从51单片机通过寄存器配置实现定时器周期计数&#xff0c;再到使用HAL库配置STM32的定时器&#xff0c;他们的实现原理都是大同小异的&#xff0c;只不过不同的平台使用的底层库不同&#xff0c;导致实现的…

公司25k招了一个测试员不会自动化,试用期没过就赶走了...

最近翻了一些网站的招聘信息&#xff0c;把一线大厂和大型互联网公司看了个遍&#xff0c;发现市场还是挺火热的&#xff0c;虽说铜三铁四&#xff0c;但是软件测试岗位并没有削减多少&#xff0c;建议大家有空还是多关注和多投简历&#xff0c;不要闭门造车&#xff0c;错过好…

手推FlinkML2.2(一)

Java 快速入门 # 本文档提供了一个关于如何使用Flink ML的快速入门。阅读本文档的用户将被指导提交一个简单的Flink作业&#xff0c;用于训练机器学习模型并提供预测服务。 求助&#xff0c;我卡住了&#xff01;# 如果你遇到困难&#xff0c;请查看社区支持资源。特别是&…