官方文档:
官方案例:https://spring.io/guides/gs/async-method
一、在spring中使用异步处理
1.@EnableAsync和@Async
首先,在配置类加上@EnableAsync来启用异步注解。
@EnableAsync//启用异步支持 @Configuration public class AppConfig { }
然后,使用@Async注解标记需要异步执行的方法。
@Async void doSomething() { // this will be run asynchronously } @Async void doSomething(String s) { // this will be run asynchronously } @Async Future<String> returnSomething(int i) { // this will be run asynchronously }
使用@Async标记的异步方法可以带参数,也可以带有返回值。返回值类型必须是Future或其子类,可以是下面几种类型:
- java.util.concurrent.Future
- org.springframework.util.concurrent.ListenableFuture(Spring提供),AsyncResult是其比较常见的实现。
- java.util.concurrent.CompletableFuture(JDK8提供)
需要说明的是,@Async默认会使用的SimpleAsyncTaskExecutor来执行,而该线程池不会复用线程。所以,通常要使用异步处理,我们都会自定义线程池。
2.Spring内置提供的线程池
Spring提供了许多TaskExecutor的内置实现。
- SyncTaskExecutor:此实现不会异步运行调用。而是,每个调用都在调用线程中进行。它主要用于不需要多线程的情况下,例如在简单的测试案例中。
- SimpleAsyncTaskExecutor:此实现不会复用线程,每次调用启动一个新线程。
- ConcurrentTaskExecutor:此实现是java.util.concurrent.Executor实例的适配器。还有一个可选的替代者ThreadPoolTaskExecutor,将Executor配置参数公开为bean属性。很少需要直接使用ConcurrentTaskExecutor。但是,如果ThreadPoolTaskExecutor不够灵活,无法满足需求时可以选择ConcurrentTaskExecutor。
- ThreadPoolTaskExecutor:此实现是最常用的。它公开了用于配置java.util.concurrent.ThreadPoolExecutor的bean属性,并将其包装在TaskExecutor中。如果需要适应其他类型的java.util.concurrent.Executor,建议您改用ConcurrentTaskExecutor
- WorkManagerTaskExecutor:此实现使用CommonJ WorkManager作为其支持服务提供者,并且是在Spring应用程序上下文中的WebLogic或WebSphere上设置基于CommonJ的线程池集成的中心便利类。
- DefaultManagedTaskExecutor:此实现在JSR-236兼容的运行时环境(例如Java EE 7+应用程序服务器)中使用JNDI获得的ManagedExecutorService,为此替换了CommonJ WorkManager。
通常情况下,我们应该使用ThreadPoolTaskExecutor,其暴露了线程池的属性供我们进行自定义设置。当ThreadPoolTaskExecutor无法满足我们时,可以使用ConcurrentTaskExecutor。
如果声明了多个线程池,则默认情况下,Spring会按照如下搜索顺序来使用线程池:
- 上下文中的唯一TaskExecutor bean。
- 否则名为“ taskExecutor”的Executor bean。
- 如果上面两者都无法处理,则将使用SimpleAsyncTaskExecutor(默认实现)处理异步方法调用。
3.自定义线程池
可以通过实现AsyncConfigurer接口或者直接继承AsyncConfigurerSupport类来自定义线程池。
1.非完全托管Spring Bean
@Configuration @EnableAsync public class AppConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(7); executor.setMaxPoolSize(42); executor.setQueueCapacity(11); executor.setThreadNamePrefix("MyExecutor-"); executor.initialize();//手动初始化 return executor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new MyAsyncUncaughtExceptionHandler(); } }
在上面的示例中,ThreadPoolTaskExecutor不是完全托管的Spring bean。
2.完全托管Spring Bean
如果要使用完全托管的Spring Bean,在getAsyncExecutor() 方法上添加@Bean注解。此时,不需要手动调用executor.initialize(),Bean在初始化之后会自动调用。
@Configuration @EnableAsync public class AppConfig implements AsyncConfigurer { @Bean @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(7); executor.setMaxPoolSize(42); executor.setQueueCapacity(11); executor.setThreadNamePrefix("MyExecutor-"); //executor.initialize();//不用手动调用 return executor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new MyAsyncUncaughtExceptionHandler(); } }
4.异常处理
当@Async方法返回类型是Future时,我们可以借助Future.get()方法抛出的异常来进行异常处理,因为异常信息被包装进了Future。
try { Future<Integer> future = asynTask.doSomething(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { //进行异常处理 System.out.println("处理异常"); }
而当返回类型是void时,异常无法捕获和不能被传递,所以就需要使用AsyncUncaughtExceptionHandler了。
public class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler { @Override public void handleUncaughtException(Throwable ex, Method method, Object... params) { // handle exception } }