@Async注解
Spring3开始提供了@Async注解,该注解可以标注在方法或者类上,从而可以方便的实现方法的异步调用。调用者在调用异步方法时将立即返回,方法的实际执行将提交给指定的线程池中的线程执行。
@Async注意事项:
- @Async标注在类上时,表示该类的所有方法都是异步方法。
- @Async注解的方法一定要通过依赖注入调用(因为要通过代理对象调用),不能直接通过this对象调用,否则不生效。
@Async使用示例
1、Spring中启用@Async
创建一个配置类,并加上@EnableAsync注解即可
@Configuration
@EnableAsync
public class SpringAsyncConfig{}
2、使用@Async
创建异步方法执行类
@Service
public class AsyncService {
// 无返回值的异步方法
@Async
public void noReturnMethod() {
String tName = Thread.currentThread().getName();
System.out.println("current thread name : " + tName);
System.out.println("noReturnMethod end");
}
// 有返回值的异步方法
@Async
public Future<String> withReturnMethod() {
String tName = Thread.currentThread().getName();
System.out.println("current thread name : " + tName);
return new AsyncResult<>("aaa");
}
}
创建调用异步方法的类
@RestController
@RequestMapping("/api/async/test/")
public class AsyncController {
@Autowired
AsyncService asyncService;
// 无返回值
@GetMapping("/noReturn")
public String noReturn() {
asyncService.noReturnMethod();
return "success";
}
// 有返回值
@GetMapping("/withReturn")
public String withReturn() {
Future<String> future = asyncService.withReturnMethod();
try {
String res = future.get();// 阻塞获取返回值
System.out.println("res = " + res);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
return "success";
}
}
Spring定义的线程池类
Spring 已经定义的线程池类有如下一些:
- SimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,默认每次调用都会创建一个新的线程。
- SyncTaskExecutor:这个类没有实现异步调用,只是一个同步操作。只适用于不需要多线程的地方。
- ConcurrentTaskExecutor:Executor的适配类,不推荐使用。如果ThreadPoolTaskExecutor不满足要求时,才用考虑使用这个类。
- SimpleThreadPoolTaskExecutor:是Quartz的SimpleThreadPool的类。线程池同时被quartz和非quartz使用,才需要使用此类。
- ThreadPoolTaskExecutor :最常使用,推荐。 其实质是对java.util.concurrent.ThreadPoolExecutor的包装。
配置自定义线程池
异步方法默认的线程池
在@EnableAsync注解中有如下注释说明:
By default, Spring will be searching for an associated thread pool definition:either a unique {@link org.springframework.core.task.TaskExecutor} bean in the context,or an {@link java.util.concurrent.Executor} bean named "taskExecutor" otherwise. Ifneither of the two is resolvable, a {@link org.springframework.core.task.SimpleAsyncTaskExecutor}will be used to process async method invocations.
翻译一下就是:
Spring首先会通过下面两种方式查找作为异步方法的默认线程池:
1、查找唯一的一个TaskExecutor类型的bean
2、或者是一个名称为“taskExecutor”的Executor类型的Bean。
如果上面两种方式都没有查找到,则使用SimpleAsyncTaskExecutor作为异步方法的默认线程池
而SimpleAsyncTaskExecutor线程池去执行@Async标注的异步方法,由于该线程池不会重用线程,所以项目中推荐使用自定义的线程池。
配置异步方法默认自定义线程池
配置@Async默认的线程池有多种方式:
- 重新实现接口AsyncConfigurer
- 继承AsyncConfigurerSupport
- 自定义一个TaskExecutor类型的bean。
- 自定义一个名称为“taskExecutor”的Executor类型的Bean。
下面展示了通过方式1(重新实现接口AsyncConfigurer)来配置默认的自定义线程池:
// 配置类实现AsyncConfigurer接口的getAsyncExecutor()方法
@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(3);//核心池大小
executor.setMaxPoolSize(6);//最大线程数
executor.setKeepAliveSeconds(60);//线程空闲时间
executor.setQueueCapacity(10);//队列程度
executor.setThreadNamePrefix("my-executor1-");//线程前缀名称
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());//配置拒绝策略
executor.setAllowCoreThreadTimeOut(true);// 允许销毁核心线程
executor.initialize();
return executor;
}
}
不同异步方法配置不同线程池
有时候不同功能的异步方法需要配置不同的线程池,可以通过在@Async上指定线程池的名称来实现
@Configuration
public class ExecutorConfig {
@Bean("customExecutor-1")// 自定义线程池1
public Executor customExecutor1() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(3);//核心池大小
executor.setMaxPoolSize(6);//最大线程数
executor.setKeepAliveSeconds(60);//线程空闲时间
executor.setQueueCapacity(10);//队列程度
executor.setThreadNamePrefix("customExecutor-1-");//线程前缀名称
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());//配置拒绝策略
executor.setAllowCoreThreadTimeOut(true);// 允许销毁核心线程
executor.initialize();
return executor;
}
@Bean("customExecutor-2")// 自定义线程池2
public Executor customExecutor2() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(3);//核心池大小
executor.setMaxPoolSize(6);//最大线程数
executor.setKeepAliveSeconds(60);//线程空闲时间
executor.setQueueCapacity(10);//队列程度
executor.setThreadNamePrefix("customExecutor-2-");//线程前缀名称
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());//配置拒绝策略
executor.setAllowCoreThreadTimeOut(true);// 允许销毁核心线程
executor.initialize();
return executor;
}
}
@Async("customExecutor-1")
public void method1(){}
@Async("customExecutor-2")
public void method2(){}
@Async的异常处理
当方法是带Future返回值的时候,Future.get()方法会抛出异常,所以异常捕获是没问题的。但是当方法是不带返回值的时候,那么此时主线程就不能捕获到异常,需要额外的配置来处理异常,可以有下面两种方式。
1、通过try-catch处理异常
直接在异步方法中使用try-catch来处理抛出的异常。这个方法也可以用于带Future返回值的异步方法。
2、通过实现AsyncUncaughtExceptionHandler接口
@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
// 省略自定义线程池的代码
}
// 自定义异常处理
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncUncaughtExceptionHandler() {
@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
System.out.println(method.getName() + "发生异常!异常原因:" + throwable.getMessage() );
}
};
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。