Spring 异步@Async注解用法 Spring @Async注解用法总结 Spring @Async基本用法示例

news2024/11/25 16:46:20

        Spring 异步@Async注解用法 Spring @Async注解用法总结 Spring @Async基本用法示例

一、概述

        在日常开发的工作中,经常会使用异步进行开发。Spring 提供一个简单的注解 @Async ,即可实现异步的开发,无需创建线程池,简单明了。 本文将整理 @Async 的常见用法,包括:基础入门,获取返回值,配置线程池,异常处理等。

        @Async 注解实现原理,请自行查看源码,从:org.springframework.aop.interceptor.AsyncExecutionInterceptor 开始...

二、简单用法

        1、Spring Boot 环境中,启动类上 使用 @EnableAsync , 即可启用 异步

        2、在类上或方法上,使用 @Async 注解,标记该方法为异步,可以通过 打印线程池名称验证。

        

@EnableAsync
@SpringBootApplication
public class SpringBootTouristApplication {

   public static void main(String[] args) {
      SpringApplication.run(SpringBootTouristApplication.class, args);
   }

    @Lazy
   @Autowired
   private SpringBootTouristApplication application;

   @Bean
   ApplicationRunner run(){
      ApplicationRunner run = (args)->{
         application.sayHi();
      };
      return run;
   }

   @Async
   public void sayHi(){
      System.out.println(Thread.currentThread().getName()+"=== async 异步");
   }
}

    

            2.1、上述代码输出结果

task-1=== async 异步

        3、@Async 注解,用在 类上,标记这个类方法都是异步的


@Service
@Async
public class MessageAsync {

   public void sendMsg(long millis){
       System.out.println(Thread.currentThread().getName()+" sendMsg start ===");
       try {
           Thread.sleep(millis);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       System.out.println(MessageAsync.class.getName() + " sendMsg 方法运行中 。。。");
       System.out.println(Thread.currentThread().getName()+" sendMsg end ===");
   }

   /**
    * @Description: 使用指定的线程池
    * @return  void
    * @version v1.0
    * @author wu
    * @date 2022/9/13 22:39
    */
    @Async("taskExecutor222")
    public void sendMsg222(long millis){
        System.out.println(Thread.currentThread().getName()+" sendMsg222 start ===");
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(MessageAsync.class.getName() + " sendMsg222 方法运行中 。。。");
        System.out.println(Thread.currentThread().getName()+" sendMsg222 end ===");
    }

}

三、获取返回值

        1、Spring 提供了统一的异步返回结果:AsyncResult ,需要注意的是:方法return的AsyncResult 对象,方法的返回需要用 Future 接收; 若使用 AsyncResult 作为返回值,会导致异常 :ClassCastException

org.springframework.scheduling.annotation.AsyncResult

        2、获取返回值,触发 ClassCastException

@Async
public AsyncResult<String> getResult(Long mill){
    System.out.println(Thread.currentThread().getName()+" 携带返回值 AsyncResult FileAsync start ===");
    try {
        Thread.sleep(mill);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(FileAsync.class.getName() + " AsyncResult 方法运行中 。。。");
    System.out.println(Thread.currentThread().getName()+" 携带返回值 AsyncResult  FileAsync end ===");
   String res = "AsyncResult 返回值,延迟"+mill+" ms";
    return new AsyncResult<String>(res);
}

        3、正常获取返回值

@Async
public Future<String> getFuture(Long mill){
    System.out.println(Thread.currentThread().getName()+" 携带返回值 Future FileAsync start ===");
    try {
        Thread.sleep(mill);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(FileAsync.class.getName() + " getFuture 方法运行中 。。。");
    System.out.println(Thread.currentThread().getName()+" 方携带返回值 Future  FileAsync end ===");
    String res = "getFuture 返回值,延迟"+mill+" ms";
    return new AsyncResult<String>(res);
}

四、配置线程池

        1、方法一:配置全局线程池


@Configuration
public class AsyncPoolConfig {

    /**
     *  核心线程数 (默认线程数)
     */
    @Value("${pool.core-size:4}")
//    @Value("${pool.core-size:1}")
    private int corePoolSize;

    /**
     *  最大线程数
     */
    @Value("${pool.max-size:8}")
//    @Value("${pool.max-size:2}")
    private int maxPoolSize;

    /**
     *  允许线程空闲时间 - 单位:秒
     */
    @Value("${pool.keep-alive:60}")
    private int keepAliveSeconds;

    /**
     *  缓冲队列数
     */
    @Value("${pool.queue-capacity:5}")
    private int queueCapacity;

    /**
     *  线程前缀名称
     */
    @Value("${thread-name-prefix: @Async-线程池pool}")
    private String threadNamePrefix;

    //设置@Async的默认线程池
    @Bean("taskExecutor")
    public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor();
        pool.setCorePoolSize(corePoolSize);//核心线程池数
        pool.setMaxPoolSize(maxPoolSize); // 最大线程数
        pool.setQueueCapacity(queueCapacity);//队列容量,当核心线程数达到最大时,新任务会放在队列中排队等待执行
        pool.setKeepAliveSeconds(keepAliveSeconds);//线程空闲时间
        pool.setAllowCoreThreadTimeOut(false);//核心线程会一直存活,即使没有任务需要执行。(默认false)时,核心线程会超时关闭
        pool.setThreadNamePrefix(threadNamePrefix);//线程前缀名称

        // 线程池的拒绝策略 --- 继续执行
//        pool.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

        // 线程池的拒绝策略 --- 抛出异常 (默认方式)
        pool.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        // 初始化
//        pool.initialize();

        return pool;
    }
}

        2、方法二:实现 AsyncConfigurer 接口 ,重写 getAsyncExecutor,配置指定线程池


@Configuration
public class AsyncConifg implements AsyncConfigurer {
    @Override
    public Executor getAsyncExecutor() {
        // 配置指定的线程池
        final ExecutorService executorService = Executors.newFixedThreadPool(10);
        return executorService;
    }
}

        3、当项目中有多个线程池时,可以通过 @Async 注解的 value属性,使用指定的线程池

// 使用 beanName为: taskExecutor222 线程池
@Async("taskExecutor222")

      

  4、注意:方法一 和 方法二的 优先级问题, 没有进行测试。

五、异常处理

        1、当异步执行的时候,遇到异常,该如何处理呢?

        2、假设如下方法,执行出现异常:

@Async
public String exp(int a, String b){
    System.out.println(Thread.currentThread().getName()+" ; exp start ===");
    Object str = null ;
    final boolean res = str.equals("");
   
    System.out.println(Thread.currentThread().getName()+" ; exp end === res = "+res);
    return "exp";
}

        2.1、输出结果如下:

task-2 ; exp start ===
[ERROR]  org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler:39  : Unexpected exception occurred invoking async method: public java.lang.String com.runcode.springboottourist.async.ExpAsync.exp(int,java.lang.String) 
java.lang.NullPointerException 
...

        2.2、注意:" task-2 ; exp end === res = " ,语句没有输出 ..

        2.3、方法体内,增加 try-catch

@Async
public String expFix(){
    System.out.println(Thread.currentThread().getName()+" ; expFix start ===");
    boolean res = false;
    try {
        Object str = null ;
        res = str.equals("");
    } catch (Exception e) {
        e.printStackTrace();
    }
    /**
     * try-catch 后:end 语句会正常输出.
     */
    System.out.println(Thread.currentThread().getName()+" ; expFix end === res = "+res);
    return "expFix";
}

        3、全局异常处理,实现 AsyncUncaughtExceptionHandler 接口,处理异常

@Configuration
public class AsyncExpConfig implements AsyncUncaughtExceptionHandler {

    @Override
    public void handleUncaughtException(Throwable ex, Method method, Object... params) {
        System.out.println("handleUncaughtException ===== start ======");
        System.out.println("异常的方法是:"+ method);
        System.out.println("exp detail info :"+ ExceptionUtils.getStackTrace(ex));
        System.out.println("参数 prams :"+ Arrays.toString(params));
        System.out.println("handleUncaughtException ===== end ======");
    }
}

        4、配置异常处理接口: 实现 AsyncConfigurer 接口 ,重写 getAsyncUncaughtExceptionHandler 方法

@Configuration
public class AsyncConifg implements AsyncConfigurer {

    @Autowired
    private AsyncExpConfig asyncExpConfig;

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return asyncExpConfig;
    }
}

        5.1、 再次执行,上述代码,异常信息变成了:

task-2 ; exp start ===
handleUncaughtException ===== start ======
异常的方法是:public java.lang.String com.runcode.springboottourist.async.ExpAsync.exp(int,java.lang.String)
exp detail info :java.lang.NullPointerException
	at com.runcode.springboottourist.async.ExpAsyn ...
 
参数 prams :[1, 22]
handleUncaughtException ===== end ====== 

六、总结

        1、本文相对详细的记录@Async 注解的常见用法,可以满足日常大部分的开发需求。

        2、注意一点: 在同一个类中,是可以存在 异步方法和非异步方法的,要注意的是调用的方式 , 比如下面代码


@Service
public class AsyncTestService {

    /**
     * @Lazy : 解决循环依赖问题 circular reference
     */
    @Lazy
    @Autowired
    private AsyncTestService asyncTestService;

    public void sync(){
        System.out.println(Thread.currentThread().getName()+" ; sync method ..");
        /**
         * 该方法实际被 this.async(); 调用,this 没有被AOP代理增强,故 不会执行 @Async 异步方法
         */
        async();
        /**
         * 该方法被 AOP增强后的方法调用,会执行 @Async 异步方法
         */
        asyncTestService.async();
    }

    @Async
    public void async() {
        System.out.println(Thread.currentThread().getName()+" ; async 异步 method ..");
    }
}

        2.1、输出结果如下:

main ; sync method ..
main ; async 异步 method ..
 @Async-线程池pool2 ; async 异步 method ..
 

        2.2、注意理解点: @Async是基于AOP实现的,普通的this调用,是没有被增强的,故而会导致方法调用无效; asyncTestService.async(); 方法调用,该类是AOP代理后增强 ... 可以通过 debug 观察 ...

 

参考资料:

Java 多线程 Runnable 与 Callable

@Lazy 注解作用

@Async 注解 实现原理 (没研究

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

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

相关文章

盘点 GitHub 上的神级指南

盘点 GitHub 上那些神级指南&#xff01;本次盘点都是 GitHub 上标星 10K 的开源指南。都是由中国的开发者开源&#xff0c;除了技术、教程类的指南&#xff0c;还有一些花里胡哨的东西。本期推荐开源项目目录&#xff1a;1. 计算机自学指南2. 大数据入门指南3. 程序员延寿指南…

[附源码]计算机毕业设计springboot企业人事管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

MASA Framework 事件总线 - 跨进程事件总线

概述 跨进程事件总线允许发布和订阅跨服务传输的消息, 服务的发布与订阅不在同一个进程中 在Masa Framework中, 跨进程总线事件提供了一个可以被开箱即用的程序 IntegrationEvents: 提供了发件箱模式 IntegrationEvents.Dapr: 借助Dapr实现了消息的发布EventLogs.EFCore: 基…

企业想要做好数据分析,可以试试瓴羊Quick BI

企业的数字化发展已经成为了一个发展的方向&#xff0c;可是各个企业如何才可以实现数字化发展确实成为了难题。从很多企业的内部发展中来讲&#xff0c;每一个部分的数据化的分析都已经成为了行业的难点&#xff0c;如何做好这些分析工作确实是很关键的内容。 而在具体数字化…

艾美捷细胞低密度脂肪酸(LDL)摄取试剂盒的功能应用

胆固醇是一种重要的细胞成分&#xff0c;维持胆固醇稳态对正常生理功能至关重要。血浆胆固醇水平升高与各种病理状况有关&#xff0c;最明显的是冠心病&#xff0c;高胆固醇水平导致动脉泡沫细胞形成和斑块堆积&#xff0c;可能导致心脏病发作或中风。细胞胆固醇代谢和血浆胆固…

教育在线学习系统,教育培训都能用,支持多个终端

随着居家隔离不断反复&#xff0c;在线教育也成为了居家学习的必备工具之一。常用的方式就是在线教育培训。教育在线学习系统为教育行业的发展提供了有效工具&#xff0c;推动着教育行业逐步转型到线上线下的教育模式。学生在家通过手机就能随时随地学习&#xff0c;非常的方便…

基于矩阵分解模型的协同过滤理论概述(涉及到SVD,SVD++,TimeSVD++)

前言 本篇文章是对博客&#xff1a;从item-base到svd再到rbm&#xff0c;多种Collaborative Filtering(协同过滤算法)从原理到实现的补全&#xff0c;感谢该作者的分享 本文补全的内容为&#xff1a; SVD中 yjy_jyj​的实际含义理解&#xff0c;以及对应的梯度下降公式TimeS…

[附源码]JAVA毕业设计高校校园社交网络(系统+LW)

[附源码]JAVA毕业设计高校校园社交网络&#xff08;系统LW&#xff09; 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术…

高通导航器软件开发包使用指南(15)

高通导航器软件开发包使用指南&#xff08;15&#xff09;9 基本参数调整9.1关键调谐参数9.2调整过程9.2.1传感器方向9.2.2电子速度控制器&#xff08;ESC&#xff09;9.2.3螺旋桨和电机特性9.2.4指定车辆总质量的参数9.2.5最小和最大推力命令9.2.6推进器配置9.2.7姿态控制增益…

魔兽世界服务端源码各个重要文件详细情况说明开服一条龙

魔兽服务端开服源文件各文件翻译 很多文件在服务器中我们知道是跟什么有关&#xff0c;但就是不知道其作用是什么。就算我们知道在这些地方中的文件都是有着不小的作用。但是由于不知道各个文件代表的是什么意思所以在面对这些文件的时候都会有无从下手的感觉&#xff0c;所以…

入耳式无线蓝牙耳机哪款好?无线入耳蓝牙耳机推荐

随着近几年蓝牙耳机的快速发展&#xff0c;使用蓝牙耳机的人也越来越多&#xff0c;可供人们选择的蓝牙耳机也有很多。那么&#xff0c;在现如今的蓝牙耳机市场中&#xff0c;哪款无线蓝牙耳机好&#xff1f;下面&#xff0c;我来给大家推荐几款无线入耳蓝牙耳机&#xff0c;可…

HTML+CSS+JavaScript仿京东购物网站制作 html静态网页设计制作 dw静态网页成品模板素材网页 web前端网页设计与制作 div静态网页设计

HTML实例网页代码, 本实例适合于初学HTML的同学。该实例里面有设置了css的样式设置&#xff0c;有div的样式格局&#xff0c;这个实例比较全面&#xff0c;有助于同学的学习,本文将介绍如何通过从头开始设计个人网站并将其转换为代码的过程来实践设计。 ⚽精彩专栏推荐&#x1…

JavaWeb中的VUE快速入门

目录 概述: Vue的安装 Vue的常用指令 通过VUE高效提交表单调用接口请求 Vue的生命周期 概述: Vue是一套前端框架&#xff0c;免除原生JavaScript中的DOM操作&#xff0c;简化书写。Vue为当前的国内前端主流框架&#xff0c;基于MVVM&#xff08;Model-View-ViewModel&…

kubernetes之pod详解

pod详解 文章目录pod详解Pod生命周期一、创建和终止二、 初始化容器Pod调度定向调度亲和性调度污点和容忍Pod生命周期 我们一般将pod对象从创建至终的这段时间范围称为pod的生命周期&#xff0c;它主要包含下面的过程&#xff1a; pod创建过程运行初始化容器&#xff08;init …

ByteX-shrink_r源码解析

背景 为什么要对R文件内联处理&#xff1f; 这里首先说一下Android R文件的产生&#xff0c;对于Android开发者我们都知道&#xff0c;当我们要使用要使用一些布局文件&#xff0c;drawable等其他资源时&#xff0c;可以直接用 R.id. R.drawble.等直接使用&#xff0c;而这个…

Redis 内存管理

前言 Redis 的同学应该都知道&#xff0c;它基于键值对&#xff08;key-value&#xff09;的内存数据库&#xff0c;所有数据存放在内存中&#xff0c;内存在 Redis 中扮演一个核心角色&#xff0c;所有的操作都是围绕它进行。我们在实际维护过程中经常会被问到如下问题&#x…

详解设计模式:组合模式

组合模式&#xff08;Composite Pattern&#xff09;&#xff0c;又叫部分整体模式&#xff0c;是 GoF 的 23 种设计模式中的一种结构型设计模式。 组合模式 是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象&#xff0c;用来表示部分以及整体层次。…

Codeforces Round #726 (Div. 2) E1. Erase and Extend (Easy Version)

翻译&#xff1a; 这是这个问题的简单版本。唯一的区别是&#x1d45b;和&#x1d458;上的约束。只有当所有版本的问题都解决了&#xff0c;你才能进行hack。 你有一个字符串&#x1d460;&#xff0c;你可以对它做两种类型的操作: 删除字符串的最后一个字符。 复制字符串:…

UI 智能化的原理和未来

本文将从 GUI 中用户体验的构建开始&#xff0c;用高质量、可调控、交互体验创新三个部分&#xff0c;分别介绍如何从传统 UI 一步步迈向 UI 智能化。最后&#xff0c;用如何实现 UI 智能化的一些思考收尾。 本文仅代表作者个人观点。前言&#xff1a;「UI 智能化才是用户体验的…

第十七章《MySQL数据库及SQL语言简介》第3节:数据库管理

17.2小节主要讲解的是MySQL数据库的下载、配置和安装。从严格意义来讲,17.2小节所做的工作是对“数据库管理系统”进行下载、安装和配置。本小节所要讲解的数据库管理是指如何用数据库管理系统新建、重命名和删除一个数据库。 程序员操作数据库管理系统主要有两种方式:1、通…