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
*/
@Async
public void asyncInvokeSimplest() {
log.info("asyncSimplest");
} /**
* 带参数的异步调用 异步方法可以传入参数
*
* @param s
*/
@Async
public void asyncInvokeWithParameter(String s) {
log.info("asyncInvokeWithParameter, parementer={}", s);
} /**
* 异常调用返回Future
*
* @param i
* @return
*/
@Async
public 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
*/
@Bean
public AsyncTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setThreadNamePrefix("Anno-Executor");
executor.setMaxPoolSize(10); // 设置拒绝策略
executor.setRejectedExecutionHandler(new RejectedExecutionHandler() {
@Override
public 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 {
@Autowired
private AsyncDemo asyncDemo; @Test
public 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:executor
id="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 {
@Autowired
private AsyncDemo asyncDemo; @Test
public 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
*/
@Async
public void asyncInvokeSimplest() {
log.info("asyncSimplest");
} /**
* 带参数的异步调用 异步方法可以传入参数
* 对于返回值是void,异常会被AsyncUncaughtExceptionHandler处理掉
* @param s
*/
@Async
public void asyncInvokeWithException(String s) {
log.info("asyncInvokeWithParameter, parementer={}", s);
throw new IllegalArgumentException(s);
} /**
* 异常调用返回Future
* 对于返回值是Future,不会被AsyncUncaughtExceptionHandler处理,需要我们在方法中捕获异常并处理
* 或者在调用方在调用Futrue.get时捕获异常进行处理
*
* @param i
* @return
*/
@Async
public 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); @Override
public 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;
} @Override
public 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 {
@Autowired
private AsyncExceptionDemo asyncDemo; @Test
public 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 之 REST

    Rest (Representational Stat Transer) 是一种软件架构风格. 基础理论 架构特性 性能 可伸缩 简化的统一接口 按需修改 组件通信透明 可移植 可靠性 架构约束 C/ ...

  2. spring boot:使用async异步线程池发送注册邮件(spring boot 2.3.1)

    一,为什么要使用async异步线程池? 1,在生产环境中,有一些需要延时处理的业务场景: 例如:发送电子邮件, 给手机发短信验证码 大数据量的查询统计 远程抓取数据等 这些场景占用时间较长,而用户又没 ...

  3. 【spring boot 系列】spring data jpa 全面解析(实践 + 源码分析)

    前言 本文将从示例.原理.应用3个方面介绍spring data jpa. 以下分析基于spring boot 2.0 + spring 5.0.4版本源码 概述 JPA是什么? JPA (Java ...

  4. Spring Boot中有多个@Async异步任务时,记得做好线程池的隔离!

    通过上一篇:配置@Async异步任务的线程池的介绍,你应该已经了解到异步任务的执行背后有一个线程池来管理执行任务.为了控制异步任务的并发不影响到应用的正常运作,我们必须要对线程池做好相应的配置,防止资 ...

  5. Spring Boot系列(一) Spring Boot准备知识

    本文是学习 Spring Boot 的一些准备知识. Spring Web MVC Spring Web MVC 的两个Context 如下图所示, 基于 Servlet 的 Spring Web M ...

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

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

  7. Spring boot 入门二:Spring Boot配置文件详解

    一.自定义属性 当我们创建一个springboot项目的时候,系统默认会为我们在src/main/java/resources目录下创建一个application.properties.同时也支持ym ...

  8. Spring Boot系列(四) Spring Cloud 之 Config Client

    Config 是通过 PropertySource 提供. 这节的内容主要是探讨配置, 特别是 PropertySource 的加载机制. Spring Cloud 技术体系 分布式配置 服务注册/发 ...

  9. Spring Boot系列(四) Spring Boot 之验证

    这节没有高深的东西, 但有一些学习思路值得借鉴. JSR 303 (Bean Validation) Maven依赖 <dependency> <groupId>org.spr ...

随机推荐

  1. SAR(遥感、卫星) 图像常用数据集

    Brazilian Coffee Scenes数据集较小,5MB左右: UC Merced Land Use Dataset(数据集规模较大,300MB+) MSTAR public targets ...

  2. 58.express安装问题:express不是内部也或者外部的命令解决方案

    转自:https://www.cnblogs.com/zhangym118/p/5842094.html "Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列 ...

  3. POJ 2226 二分图最小覆盖

    题意: 思路: 把横着的连通块放在一个集合 竖着的放在一个集合 如果有交 就连边 求最小覆盖即可 (数值上等于最大匹配) //By SiriusRen #include <cstdio> ...

  4. Android Error:(1, 0) Gradle version 2.2 is required. Current version is 3.4.1.

    我们在使用Android Studio的时候新建项目没什么问题的,但是当我们下载一份Github上的项目导入进去的时候却没法正常使用,其原因主要是gradle配置问题.而由于gradle这个玩意儿版本 ...

  5. CTF编程题-三羊献瑞(实验吧)解题随记

    题目如下.解题步骤参考的是https://cloud.tencent.com/developer/news/373865中作者的思路. 1.首先,两个四位数相加等于一个五位数,那么这个五位数的第一位必 ...

  6. 如何优雅的写UI——(4)选项卡美化

    现在做出来的选项卡实在太丑的,咱们怎么把他弄得好看一点呢 tabctrl是可以添加icon图标的,那派生与tabctrl的mfctabctrl肯定也能添加图标,他们两个添加图标的原理一样,但是还是有点 ...

  7. Android基础新手教程——3.8 Gestures(手势)

    Android基础新手教程--3.8 Gesture(手势) 标签(空格分隔): Android基础新手教程 本节引言: 周六不歇息,刚剪完了个大平头回来.继续码字~ 好的,本节给大家带来点的是第三章 ...

  8. DIV+CSS两种盒子模型(W3C盒子与IE盒子)

    在辨析两种盒子模型之前.先简单说明一下什么叫盒子模型. 原理: 先说说我们在网页设计中常听的属性名:内容(content).填充(padding).边框(border).边界(margin), CSS ...

  9. OpenStack_Swift源代码分析——ObjectReplicator源代码分析(1)

    1.ObjectorReplicator的启动 首先执行启动脚本 swift-init object-replicator start 此执行脚本的执行过程和ring执行脚本执行过程差点儿相同.找到s ...

  10. [NOI.AC#41]最短路 线性基

    链接 题解 如果不加边,两个点之间的长度是唯一的(只能走最短路径),因为如果重复走,就异或掉了. 因此,先DFS预处理一下每个点到根的距离 \(d[x]\) ,那么 \(x,y\) 之间的距离为 $d ...