前言
当我们需要异步执行某个方法时,最常用的方法就是新建一个线程去执行任务或者把任务提交到线程池异步执行。在spring boot,最简单的方式就是使用 @Async注解。下面我们就来体验下 @Async注解
1、开启异步异步调用
在启动类添加@EnableAsync注解即可开启异步调用
@SpringBootApplication
@MapperScan("com.example.testdemo.mapper")
@EnableAsync
public class TestdemoApplication {
}
2、定义异步任务
使用@Async注解定义异步任务:我们只需要在一个方法上使用@Async注解,该方法就定义为异步方法了
异步的方法有3种
- 最简单的异步调用,返回值为void
- 带参数的异步调用 异步方法可以传入参数
- 异常调用返回Future
下面是最常见的2种异步方法
@Component
public class AsyncDemo {
/**
* 普通异步调用
*
* @param parameter
*/
@Async
public void asyncMethod(String parameter) {
System.out.println("入参为:"+parameter);
}
/**
* 异常调用返回Future
* @param parameter
* @return
*/
@Async
public Future<String> asyncFutureMethod(String parameter) {
Future<String> future= new AsyncResult<String>("success:" + parameter);
return future;
}
}
常见TaskExecutor
Spring发行版中包含了许多预先构建的TaskExecutor实现,以下是spring的TaskExecutor实现类
ThreadPoolTaskExecutor
ThreadPoolTaskExecutor是使用ThreadPoolExecutor实现的,即是ThreadPoolExecutor的Spring包装。
ThreadPoolTaskExecutor同样是提供线程池执行任务,但是可以使用xml或者JavaBean的形式进行配置,初始化。
SimpleAsyncTaskExecutor
此实现不重用任何线程,而是为每次调用启动一个新线程。但是,它支持一个并发限制,该限制将阻塞任何超过该限制的调用,直到释放一个槽为止。
不是真的线程池,只是不重用线程,每次调用都会创建一个新的线程。
SyncTaskExecutor
此实现不异步执行调用。相反,每次调用都在调用线程中进行。它主要用于不需要多线程的情况下,比如在简单的测试用例中。
ConcurrentTaskExecutor
Executor的适配类,不推荐使用。如果ThreadPoolTaskExecutor不满足要求时,才用考虑使用这个类。
Async默认使用的是 SimpleAsyncTaskExecutor
3、自定义Async配置
我们可以通过实现AsyncConfigurer来自定义一些异步配置,比如使用的线程池、异常处理
面是一个自定义的AsyncConfigurer配置,大家可以参考
@Slf4j
@Configuration
public class MyAsyncConfigurer implements AsyncConfigurer {
/**
* 使用的线程池
* @return
*/
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
//设置核心线程数
threadPoolTaskExecutor.setCorePoolSize(2);
//设置最大线程数
threadPoolTaskExecutor.setMaxPoolSize(6);
//设置队列长度
threadPoolTaskExecutor.setQueueCapacity(10);
//设置超出核心线程数的空闲线程存活时间,秒为单位,默认60
threadPoolTaskExecutor.setKeepAliveSeconds(60);
//设置线程名前缀
threadPoolTaskExecutor.setThreadNamePrefix("MyAsync-");
//线程池的饱和策略 我这里设置的是 CallerRunsPolicy 也就是由用调用者所在的线程来执行任务 共有四种
//AbortPolicy:直接抛出异常,这是默认策略;
//CallerRunsPolicy:用调用者所在的线程来执行任务;
//DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
//DiscardPolicy:直接丢弃任务;
threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//设置在关闭线程池时是否等待任务完成
threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);
//设置等待终止的秒数
threadPoolTaskExecutor.setAwaitTerminationSeconds(60);
return threadPoolTaskExecutor;
}
/**
* 异常处理
* @return
*/
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (Throwable throwable, Method method, Object... obj)->{
log.info("Method name -{}, Exception message -{}" ,method.getName(),throwable);
};
}
}