Spring异步线程池的接口类,其实质是java.util.concurrent.Executorjava
Spring 已经实现的异常线程池:
1. SimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,每次调用都会建立一个新的线程。
2. SyncTaskExecutor:这个类没有实现异步调用,只是一个同步操做。只适用于不须要多线程的地方
3. ConcurrentTaskExecutor:Executor的适配类,不推荐使用。若是ThreadPoolTaskExecutor不知足要求时,才用考虑使用这个类
4. SimpleThreadPoolTaskExecutor:是Quartz的SimpleThreadPool的类。线程池同时被quartz和非quartz使用,才须要使用此类
5. ThreadPoolTaskExecutor :最常使用,推荐。 其实质是对java.util.concurrent.ThreadPoolExecutor的包装git
spring对过@Async定义异步任务github
异步的方法有3种
1. 最简单的异步调用,返回值为void
2. 带参数的异步调用 异步方法能够传入参数
3. 异常调用返回Futurespring
详细见代码:markdown
@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());
Spring有两种方法启动配置
1. 注解
2. XMLapp
要启动异常方法还须要如下配置
1. @EnableAsync 此注解开户异步调用功能
2. public AsyncTaskExecutor taskExecutor() 方法自定义本身的线程池,线程池前缀”Anno-Executor”。若是不定义,则使用系统默认的线程池。less
@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,可见异常线程池起做用了async
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
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
在调用方法时,可能出现方法中抛出异常的状况。在异步中主要有有两种异常处理方法:
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
简单几步,实现异步新线程调用。
一、在主类中添加@EnableAsync注解:
能够看到,没有自定义的Executor,因此使用缺省的TaskExecutor 。
前面是最简单的使用方法。若是想使用自定义的Executor,能够按照以下几步来:
一、新建一个Executor配置类,顺便把@EnableAsync注解搬到这里来:
这里定义了两个不一样的Executor,第二个从新设置了pool已经达到max size时候的处理方法;同时指定了线程名字的前缀。
二、自定义Executor的使用:
三、测试(测试用例不变)结果:
源代码:https://github.com/xujijun/my-spring-boot
引言:Spring做为容器为咱们托管对象,可是有时咱们须要多线程执行任务,那么咱们该如何配置呢?
解决:利用java的线程池Executor执行任务
步骤
1.配置TaskExecutor
这里直接将线程池注入
CorePoolSize表明执行任务的线程数量
public class TaskExecutorConfig implements AsyncConfigurer{//实现AsyncConfigurer接口 @Bean public Executor getAsyncExecutor() {//实现AsyncConfigurer接口并重写getAsyncExecutor方法,并返回一个ThreadPoolTaskExecutor,这样咱们就得到了一个基于线程池TaskExecutor ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); taskExecutor.setCorePoolSize(5); taskExecutor.setMaxPoolSize(10); taskExecutor.setQueueCapacity(25); taskExecutor.initialize(); return taskExecutor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return null; } }
这里咱们获得了一个基于java的线程池Executer的线程池,而后设置了部分参数,返回了一个实例
2.编写咱们须要执行的任务,并注明service
@Service @Slf4j public class AsyncTaskService { @Async public void dataTranslate(int i) { log.info("启动了线程"+i); } }
这里用日志打印
3.将线程池对象注入,并调用任务service。
最后在application中开始异步支持@EnableAsync
调用结果:能够看到,是不一样的线程执行了打印任务,并且根据cpu时间片,抢占,能够看到线程执行顺序也发生了变化,说明是异步执行
总结:Spring Boot对多线程的支持和Spring没什么两样,就是须要先配置线程池,而后注入bean,再写异步方法,最后调用就能够了。
关于多线程中还有许多问题,如线程同步等就须要在写代码时多注意多思考了。另外,合理配置线程池参数也很重要