转载 自 https://blog.csdn.net/hry2015/article/details/67640534

1. TaskExecutor
Spring异步线程池的接口类,其实质是java.util.concurrent.Executor

Spring 已经实现的异常线程池: 
1. SimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程。 
2. SyncTaskExecutor:这个类没有实现异步调用,只是一个同步操作。只适用于不需要多线程的地方 
3. ConcurrentTaskExecutor:Executor的适配类,不推荐使用。如果ThreadPoolTaskExecutor不满足要求时,才用考虑使用这个类 
4. SimpleThreadPoolTaskExecutor:是Quartz的SimpleThreadPool的类。线程池同时被quartz和非quartz使用,才需要使用此类 
5. ThreadPoolTaskExecutor :最常使用,推荐。 其实质是对java.util.concurrent.ThreadPoolExecutor的包装

2. @Async
spring对过@Async定义异步任务

异步的方法有3种 
1. 最简单的异步调用,返回值为void 
2. 带参数的异步调用 异步方法可以传入参数 
3. 异常调用返回Future

详细见代码:

@Component
public class AsyncDemo {private static final Logger log = LoggerFactory.getLogger(AsyncDemo.class);/*** 最简单的异步调用,返回值为void*/@Asyncpublic void asyncInvokeSimplest() {log.info("asyncSimplest");}/*** 带参数的异步调用 异步方法可以传入参数* * @param s*/@Asyncpublic void asyncInvokeWithParameter(String s) {log.info("asyncInvokeWithParameter, parementer={}", s);}/*** 异常调用返回Future* * @param i* @return*/@Asyncpublic Future<String> asyncInvokeReturnFuture(int i) {log.info("asyncInvokeReturnFuture, parementer={}", i);Future<String> future;try {Thread.sleep(1000 * 1);future = new AsyncResult<String>("success:" + i);} catch (InterruptedException e) {future = new AsyncResult<String>("error");}return future;}}

以上的异步方法和普通的方法调用相同

asyncDemo.asyncInvokeSimplest();
asyncDemo.asyncInvokeWithException("test");
Future<String> future = asyncDemo.asyncInvokeReturnFuture(100);
System.out.println(future.get());

3. Spring 开启异步配置
Spring有两种方法启动配置 
1. 注解 
2. XML

3.1 通过注解实现
要启动异常方法还需要以下配置 
1. @EnableAsync 此注解开户异步调用功能 
2. public AsyncTaskExecutor taskExecutor() 方法自定义自己的线程池,线程池前缀”Anno-Executor”。如果不定义,则使用系统默认的线程池。

@SpringBootApplication
@EnableAsync // 启动异步调用
public class AsyncApplicationWithAnnotation {private static final Logger log = LoggerFactory.getLogger(AsyncApplicationWithAnnotation.class);/*** 自定义异步线程池* @return*/@Beanpublic AsyncTaskExecutor taskExecutor() {  ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setThreadNamePrefix("Anno-Executor");executor.setMaxPoolSize(10);  // 设置拒绝策略executor.setRejectedExecutionHandler(new RejectedExecutionHandler() {@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {// .....}});// 使用预定义的异常处理类// executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());return executor;  } public static void main(String[] args) {log.info("Start AsyncApplication.. ");SpringApplication.run(AsyncApplicationWithAnnotation.class, args);}
}

以上的异常方法和普通的方法调用相同

@RunWith(SpringRunner.class)
@SpringBootTest(classes=AsyncApplicationWithAnnotation.class)
public class AsyncApplicationWithAnnotationTests {@Autowiredprivate AsyncDemo asyncDemo;@Testpublic void contextLoads() throws InterruptedException, ExecutionException {asyncDemo.asyncInvokeSimplest();asyncDemo.asyncInvokeWithParameter("test");Future<String> future = asyncDemo.asyncInvokeReturnFuture(100);System.out.println(future.get());}
}

执行测试用例,输出内容如下: 
可以看出主线程的名称为main; 异步方法则使用 Anno-Executor1,可见异常线程池起作用了

2017-03-28 20:00:07.731  INFO 5144 --- [ Anno-Executor1] c.hry.spring.async.annotation.AsyncDemo  : asyncSimplest
2017-03-28 20:00:07.732  INFO 5144 --- [ Anno-Executor1] c.hry.spring.async.annotation.AsyncDemo  : asyncInvokeWithParameter, parementer=test
2017-03-28 20:00:07.751  INFO 5144 --- [ Anno-Executor1] c.hry.spring.async.annotation.AsyncDemo  : asyncInvokeReturnFuture, parementer=100
success:100
2017-03-28 20:00:08.757  INFO 5144 --- [       Thread-2] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@47af7f3d: startup date [Tue Mar 28 20:00:06 CST 2017]; root of context hierarchy

3.2 通过XML实现

Bean文件配置: spring_async.xml 
1. 线程的前缀为xmlExecutor 
2. 启动异步线程池配置

    <!-- 等价于 @EnableAsync, executor指定线程池 --><task:annotation-driven executor="xmlExecutor"/><!-- id指定线程池产生线程名称的前缀 --><task:executorid="xmlExecutor"pool-size="5-25"queue-capacity="100"keep-alive="120"rejection-policy="CALLER_RUNS"/>

线程池参数说明 
1. ‘id’ : 线程的名称的前缀 
2. ‘pool-size’:线程池的大小。支持范围”min-max”和固定值(此时线程池core和max sizes相同) 
3. ‘queue-capacity’ :排队队列长度 
○ The main idea is that when a task is submitted, the executor will first try to use a free thread if the number of active threads is currently less than the core size. 
○ If the core size has been reached, then the task will be added to the queue as long as its capacity has not yet been reached.
○ Only then, if the queue’s capacity has been reached, will the executor create a new thread beyond the core size. 
○ If the max size has also been reached, then the executor will reject the task. 
○ By default, the queue is unbounded, but this is rarely the desired configuration because it can lead to OutOfMemoryErrors if enough tasks are added to that queue while all pool threads are busy. 
4. ‘rejection-policy’: 对拒绝的任务处理策略 
○ In the default ThreadPoolExecutor.AbortPolicy, the handler throws a runtime RejectedExecutionException upon rejection. 
○ In ThreadPoolExecutor.CallerRunsPolicy, the thread that invokes execute itself runs the task. This provides a simple feedback control mechanism that will slow down the rate that new tasks are submitted. 
○ In ThreadPoolExecutor.DiscardPolicy, a task that cannot be executed is simply dropped. 
○ In ThreadPoolExecutor.DiscardOldestPolicy, if the executor is not shut down, the task at the head of the work queue is dropped, and then execution is retried (which can fail again, causing this to be repeated.) 
5. ‘keep-alive’ : 线程保活时间(单位秒) 
setting determines the time limit (in seconds) for which threads may remain idle before being terminated. If there are more than the core number of threads currently in the pool, after waiting this amount of time without processing a task, excess threads will get terminated. A time value of zero will cause excess threads to terminate immediately after executing a task without remaining follow-up work in the task queue()

异步线程池

@SpringBootApplication
@ImportResource("classpath:/async/spring_async.xml")
public class AsyncApplicationWithXML {private static final Logger log = LoggerFactory.getLogger(AsyncApplicationWithXML.class);public static void main(String[] args) {log.info("Start AsyncApplication.. ");SpringApplication.run(AsyncApplicationWithXML.class, args);}
}

测试用例

@RunWith(SpringRunner.class)
@SpringBootTest(classes=AsyncApplicationWithXML.class)
public class AsyncApplicationWithXMLTest {@Autowiredprivate AsyncDemo asyncDemo;@Testpublic void contextLoads() throws InterruptedException, ExecutionException {asyncDemo.asyncInvokeSimplest();asyncDemo.asyncInvokeWithParameter("test");Future<String> future = asyncDemo.asyncInvokeReturnFuture(100);System.out.println(future.get());}
}

运行测试用例,输出内容如下: 
可以看出主线程的名称为main; 异步方法则使用 xmlExecutor-x,可见异常线程池起作用了

2017-03-28 20:12:10.540  INFO 12948 --- [           main] c.h.s.a.xml.AsyncApplicationWithXMLTest  : Started AsyncApplicationWithXMLTest in 1.441 seconds (JVM running for 2.201)
2017-03-28 20:12:10.718  INFO 12948 --- [  xmlExecutor-2] com.hry.spring.async.xml.AsyncDemo       : asyncInvokeWithParameter, parementer=test
2017-03-28 20:12:10.721  INFO 12948 --- [  xmlExecutor-1] com.hry.spring.async.xml.AsyncDemo       : asyncSimplest
2017-03-28 20:12:10.722  INFO 12948 --- [  xmlExecutor-3] com.hry.spring.async.xml.AsyncDemo       : asyncInvokeReturnFuture, parementer=100
success:100
2017-03-28 20:12:11.729  INFO 12948 --- [       Thread-2] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@71809907: startup date [Tue Mar 28 20:12:09 CST 2017]; root of context hierarchy

4. 对异步方法的异常处理
在调用方法时,可能出现方法中抛出异常的情况。在异步中主要有有两种异常处理方法: 
1. 对于方法返回值是Futrue的异步方法: a) 一种是在调用future的get时捕获异常; b) 在异常方法中直接捕获异常 
2. 对于返回值是void的异步方法:通过AsyncUncaughtExceptionHandler处理异常

AsyncExceptionDemo:

@Component
public class AsyncExceptionDemo {private static final Logger log = LoggerFactory.getLogger(AsyncExceptionDemo.class);/*** 最简单的异步调用,返回值为void*/@Asyncpublic void asyncInvokeSimplest() {log.info("asyncSimplest");}/*** 带参数的异步调用 异步方法可以传入参数*  对于返回值是void,异常会被AsyncUncaughtExceptionHandler处理掉* @param s*/@Asyncpublic void asyncInvokeWithException(String s) {log.info("asyncInvokeWithParameter, parementer={}", s);throw new IllegalArgumentException(s);}/*** 异常调用返回Future*  对于返回值是Future,不会被AsyncUncaughtExceptionHandler处理,需要我们在方法中捕获异常并处理*  或者在调用方在调用Futrue.get时捕获异常进行处理* * @param i* @return*/@Asyncpublic Future<String> asyncInvokeReturnFuture(int i) {log.info("asyncInvokeReturnFuture, parementer={}", i);Future<String> future;try {Thread.sleep(1000 * 1);future = new AsyncResult<String>("success:" + i);throw new IllegalArgumentException("a");} catch (InterruptedException e) {future = new AsyncResult<String>("error");} catch(IllegalArgumentException e){future = new AsyncResult<String>("error-IllegalArgumentException");}return future;}}

实现AsyncConfigurer接口对异常线程池更加细粒度的控制 
a) 创建线程自己的线程池 
b) 对void方法抛出的异常处理的类AsyncUncaughtExceptionHandler

/*** 通过实现AsyncConfigurer自定义异常线程池,包含异常处理* * @author hry**/
@Service
public class MyAsyncConfigurer implements AsyncConfigurer{private static final Logger log = LoggerFactory.getLogger(MyAsyncConfigurer.class);@Overridepublic Executor getAsyncExecutor() {ThreadPoolTaskExecutor threadPool = new ThreadPoolTaskExecutor();  threadPool.setCorePoolSize(1);  threadPool.setMaxPoolSize(1);  threadPool.setWaitForTasksToCompleteOnShutdown(true);  threadPool.setAwaitTerminationSeconds(60 * 15);  threadPool.setThreadNamePrefix("MyAsync-");threadPool.initialize();return threadPool;  }@Overridepublic AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return new MyAsyncExceptionHandler();  }/*** 自定义异常处理类* @author hry**/class MyAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {  @Override  public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {  log.info("Exception message - " + throwable.getMessage());  log.info("Method name - " + method.getName());  for (Object param : obj) {  log.info("Parameter value - " + param);  }  }  } }
@SpringBootApplication
@EnableAsync // 启动异步调用
public class AsyncApplicationWithAsyncConfigurer {private static final Logger log = LoggerFactory.getLogger(AsyncApplicationWithAsyncConfigurer.class);public static void main(String[] args) {log.info("Start AsyncApplication.. ");SpringApplication.run(AsyncApplicationWithAsyncConfigurer.class, args);}}

测试代码

@RunWith(SpringRunner.class)
@SpringBootTest(classes=AsyncApplicationWithAsyncConfigurer.class)
public class AsyncApplicationWithAsyncConfigurerTests {@Autowiredprivate AsyncExceptionDemo asyncDemo;@Testpublic void contextLoads() throws InterruptedException, ExecutionException {asyncDemo.asyncInvokeSimplest();asyncDemo.asyncInvokeWithException("test");Future<String> future = asyncDemo.asyncInvokeReturnFuture(100);System.out.println(future.get());}
}

运行测试用例 
MyAsyncConfigurer 捕获AsyncExceptionDemo 对象在调用asyncInvokeWithException的异常

2017-04-02 16:01:45.591  INFO 11152 --- [      MyAsync-1] c.h.s.a.exception.AsyncExceptionDemo     : asyncSimplest
2017-04-02 16:01:45.605  INFO 11152 --- [      MyAsync-1] c.h.s.a.exception.AsyncExceptionDemo     : asyncInvokeWithParameter, parementer=test
2017-04-02 16:01:45.608  INFO 11152 --- [      MyAsync-1] c.h.s.async.exception.MyAsyncConfigurer  : Exception message - test
2017-04-02 16:01:45.608  INFO 11152 --- [      MyAsync-1] c.h.s.async.exception.MyAsyncConfigurer  : Method name - asyncInvokeWithException
2017-04-02 16:01:45.608  INFO 11152 --- [      MyAsync-1] c.h.s.async.exception.MyAsyncConfigurer  : Parameter value - test
2017-04-02 16:01:45.608  INFO 11152 --- [      MyAsync-1] c.h.s.a.exception.AsyncExceptionDemo     : asyncInvokeReturnFuture, parementer=100
error-IllegalArgumentException
2017-04-02 16:01:46.656  INFO 11152 --- [       Thread-2] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@47af7f3d: startup date [Sun Apr 02 16:01:44 CST 2017]; root of context hierarchy

5. 源码地址

代码的GITHUB地址

Spring Boot系列二 Spring @Async异步线程池用法总结相关推荐

  1. Spring Boot系列六 Spring boot集成mybatis、分页插件pagehelper

    1. 概述 本文的内容包括如下内容: Spring Boot集成mybatis Spring Boot集成pagehelper分页插件,定义分页的相关类 实现工具类:model转dto,实现数据层和传 ...

  2. spring boot 学习(二)spring boot 框架整合 thymeleaf

    spring boot 框架整合 thymeleaf spring boot 的官方文档中建议开发者使用模板引擎,避免使用 JSP.因为若一定要使用 JSP 将无法使用. 注意:本文主要参考学习了大神 ...

  3. Spring Data 系列(二) Spring+JPA入门(集成Hibernate)

    通过[Spring Data 系列(一) 入门]的介绍,通过对比的方式认识到Spring提供的JdbcTemplate的强大功能.通过使用JdbcTemplate,操作数据库,不需要手动处理Conne ...

  4. Spring Boot系列四 Spring @Value 属性注入使用总结一

    @Value注入 不通过配置文件的注入属性的情况 通过@Value将外部的值动态注入到Bean中,使用的情况有: 注入普通字符串 注入操作系统属性 注入表达式结果 注入其他Bean属性:注入beanI ...

  5. java 微服务实践 视频,全新JAVA微服为务实战Spring Boot系列视频教程 小马哥 JAVA微服务实践视频课程...

    全新JAVA微服为务实战Spring Boot系列视频教程 小马哥 JAVA微服务实践视频课程 ===============课程目录=============== ├─(1) 03Java 微服务实 ...

  6. java小马哥mercyblitz,小马哥 Java 微服务实践 - Spring Boot 系列

    资源介绍 教程名称:小马哥 Java 微服务实践 - Spring Boot 系列 教程目录: 03Java 微服务实践 - Spring Boot 系列(三)Web篇(中) 04Java 微服务实践 ...

  7. java小马哥springboot_小马哥Spring Boot 系列Java微服务实战视频教程

    链接失效或更多好课请联系微信 ZA_summer 01.Java 微服务实践 – Spring Boot 系列(一)初体验 02.Java 微服务实践 – Spring Boot 系列(二) Web篇 ...

  8. 国内最全的Spring Boot系列之二

    历史文章 <国内最全的Spring Boot系列之一> 视频&交流平台 SpringBoot视频:http://t.cn/R3QepWG Spring Cloud视频:http:/ ...

  9. 国内最全的Spring Boot系列之三

    历史文章 <国内最全的Spring Boot系列之一> <国内最全的Spring Boot系列之二> 马上要过年了,作者要回家好好休息一下了,吃饱喝足,明年继续.在此和大家拜个 ...

最新文章

  1. 【c语言】蓝桥杯算法提高 算术运算
  2. Oracle Database 快捷版 安装 连接
  3. 我的游戏学习日志46——游戏交互设计(2)
  4. Kafka科普系列 | 原来Kafka中的选举有这么多?
  5. scala的多种集合的使用(6)之映射Map的操作方法
  6. 明晚来墨天轮直播间,听杨长老聊聊Oracle数据库并行的是是非非
  7. 2021年双十一大复盘:众人唱衰双十一,我们却发现了这些机会
  8. MUI开发指南(二) webview对象
  9. 机器学习笔记(十五):人脸识别
  10. sci协议集群服务器,唠嗑|新出了关于SCI的规定,可我连SCI分区都没搞明白
  11. 国军标GJB 151B-2013军用设备和分系统电磁发射和敏感度要求与测量(海军10项)
  12. vmware各个版本 安装 mac osx 10.11终级权威教程,踩了所有坑
  13. javascript运行机制
  14. ssh远程出错Add correct host key in /home/xxx/.ssh/known_hosts to get rid of this message
  15. C++ Win32程序编写入门
  16. TextRank算法实践
  17. Java入门篇——零基础如何学习Java
  18. 使IE浏览器支持webp格式图片显示
  19. Rolling back JDBC Connection [org.sqlite.jdbc4.JDBC4Connection
  20. 3维空间中点、线、面之间的数学关系(python代码)

热门文章

  1. rapidjson官方教程
  2. 基于原始套接字的嗅探器
  3. 通过WM_COPYDATA消息完成进程间通信
  4. 字符串匹配算法(三):KMP(KnuthMorrisPratt)算法
  5. 面试官:GET和POST两种基本请求方法有什么区别
  6. RabbitMQ管理(3)——Web端管理
  7. 给你一个能生成1到5随机数的函数,用它写一个函数生成1到7的随机数
  8. 视频生产环境下的音视频解决方案
  9. CABR:Beamer的内容自适应速率控制算法
  10. LiveVideoStack冬季招聘(高级策划编辑,市场BD主管)