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@Autowiredprivate SpringBootTouristApplication application;@BeanApplicationRunner run(){ApplicationRunner run = (args)->{application.sayHi();};return run;}@Asyncpublic 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 {@Overridepublic 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 {@Overridepublic 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 {@Autowiredprivate AsyncExpConfig asyncExpConfig;@Overridepublic 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.NullPointerExceptionat com.runcode.springboottourist.async.ExpAsyn ...参数 prams :[1, 22]
handleUncaughtException ===== end ====== 

六、总结

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

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


@Service
public class AsyncTestService {/*** @Lazy : 解决循环依赖问题 circular reference*/@Lazy@Autowiredprivate AsyncTestService asyncTestService;public void sync(){System.out.println(Thread.currentThread().getName()+" ; sync method ..");/*** 该方法实际被 this.async(); 调用,this 没有被AOP代理增强,故 不会执行 @Async 异步方法*/async();/*** 该方法被 AOP增强后的方法调用,会执行 @Async 异步方法*/asyncTestService.async();}@Asyncpublic 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 注解 实现原理 (没研究)

Spring 异步@Async注解用法 Spring @Async注解用法总结 Spring @Async基本用法示例相关推荐

  1. Spring异步Async和事务Transactional注解

    Spring开发中我们我们常常用到@Transaction和@Async,但这2个注解加在一起很多的开发者不敢用,担心事务不生效.下面我们就仔细讲解一下这2个注解同时运用,文章用3个场景讲述它们之间的 ...

  2. async异步注解和aspect切面注解等注解的原理

    在我们使用spring框架的过程中,在很多时候我们会使用@async注解来异步执行某一些方法,提高系统的执行效率.今天我们来探讨下spring是如何完成这个功能的. 1.spring 在扫描bean的 ...

  3. Spring学习总结(29)——Spring异步处理@Async的使用以及原理、源码分析(@EnableAsync)

    在开发过程中,我们会遇到很多使用线程池的业务场景,例如异步短信通知.异步记录操作日志.大多数使用线程池的场景,就是会将一些可以进行异步操作的业务放在线程池中去完成.例如在生成订单的时候给用户发送短信, ...

  4. Spring系列(三):@ComponentScan注解用法介绍

    今天给大家分享Spring中@ComponentScan注解的用法,希望对大家能有所帮助! 1.@ComponentScan注解的作用 @ComponentScan注解一般和@Configuratio ...

  5. Spring基础专题——第九章(基础注解编程——上)

    目标,去年一年比较懒吧,所以今年我希望我的知识可以分享给正在奋斗中的互联网开发人员,以及未来想往架构师上走的道友们我们一起进步,从一个互联网职场小白到一个沪漂湿人,一路让我知道分享是一件多么重要的事情 ...

  6. 【EventBus】Subscribe 注解分析 ( Subscribe 注解属性 | threadMode 线程模型 | POSTING | MAIN | MAIN_ORDERED | ASYNC)

    文章目录 一.Subscribe 注解属性 二.threadMode 线程模式 ( POSTING | MAIN | MAIN_ORDERED | BACKGROUND | ASYNC ) 一.Sub ...

  7. 面向切面(AOP)之Spring接口方式 schema配置方式 aspectj注解方式

    一.初识AOP   关于AOP的学习可以参看帮助文档:spring-3.2.0.M2\docs\reference\html目录下index.html的相关章节      1.AOP:Aspect-O ...

  8. spring,mybatis事务管理配置与@Transactional注解使用[转]

    spring,mybatis事务管理配置与@Transactional注解使用[转] spring,mybatis事务管理配置与@Transactional注解使用 概述 事务管理对于企业应用来说是至 ...

  9. Spring学习第6篇: 基于注解使用IOC

    大家家好,我是一名网络怪咖,北漂五年.相信大家和我一样,都有一个大厂梦,作为一名资深Java选手,深知Spring重要性,现在普遍都使用SpringBoot来开发,面试的时候SpringBoot原理也 ...

最新文章

  1. UC伯克利发现「没有免费午餐定理」加强版:每个神经网络,都是一个高维向量...
  2. nagios监控单网卡双IP
  3. HDU 4616 Game 树形DP
  4. 如何在用例之间传递值_如何从0搭建自己的自动化测试体系
  5. python中deepcopy函数_Python学习笔记函数之copy()和deepcopy()
  6. 供应链服务产业数据知多少
  7. 2020国内软件测试机构排名
  8. [时间复杂度]为什么采用二叉排序树查找的平均查找长度为O(log2n)
  9. 前端经典面经--助你金九银十面试无烦恼
  10. 4K秒开,稀缺宝藏影视APP!
  11. dva的简单使用(一)
  12. 树、二叉树(完全二叉树、满二叉树)概念图解
  13. hiho 1051 : 补提交卡
  14. 【python-docx】长度单位(毫米、厘米、英尺、磅等)的表示和转换关系
  15. 《放学后》—— 读后总结
  16. 中国大学MOOC课程《Python语言程序设计》课后练习第一周
  17. 30分钟搞懂 RocketMQ原理
  18. 蓝色微立体图表合集4PPT模板
  19. 地图服务“新基建”决胜AI,百度地图如何再度领先行业?
  20. 金融量化-技术分析策略和交易系统_CCI指标的策略实现

热门文章

  1. 人世间,多少好,最后成了彼此的负累
  2. Alienware 17R5 Ubuntu 16.04显卡驱动
  3. symfony nusoap complextype
  4. 2022.06.01-2022.06.05
  5. matlab产生电弧信号,Matlab7.0下电弧模型的建立与分析
  6. 视频点播开发者实战:视频水印的基本使用
  7. java计算机毕业设计家庭饮食营养管理源码+mysql数据库+系统+lw文档+部署(2)
  8. JavaScript数据结构与算法
  9. AndroidKiller连接手机模拟器
  10. java stringbuilder 清空问题