在Springboot中通常会使用@Async注解开启一个异步线程来执行异步任务,本文主要讨论该注解的使用示例及在使用过程中的注意事项。
@Async
public void asyncMethod(){
//do something
}
通常情况下直接使用@EnableAsync和@Async注解就可以实现任务的异步执行,但如果未配置@Async注解的value属性默认会使用内部的SimpleAsyncTaskExecutor来创建线程并执行任务。默认情况下该线程池是来一个任务创建一个线程,若系统中不断的创建线程,最终会导致系统占用内存过高,引发OutOfMemoryError错误。
针对线程创建问题,SimpleAsyncTaskExecutor提供了限流机制,通过concurrencyLimit属性来控制开关,当concurrencyLimit>=0时开启限流机制,默认关闭限流机制即concurrencyLimit=-1,当关闭情况下,会不断创建新的线程来处理任务。基于默认配置SimpleAsyncTaskExecutor并不是严格意义的线程池,达不到线程复用的目的。
使用自定义线程池示例如下:
//配置自定义的线程池
@Configuration
@EnableAsync
public class AppConfig {
public static final String ASYNC_EXECUTOR_NAME = "asyncExecutor";
@Bean(name = ASYNC_EXECUTOR_NAME)
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(3);
executor.setMaxPoolSize(5);
executor.setQueueCapacity(100);
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setThreadNamePrefix("AsyncThread-");
executor.setTaskDecorator(runnable -> {
RequestAttributes context = RequestContextHolder.getRequestAttributes();
return () -> {
try {
RequestContextHolder.setRequestAttributes(context);
runnable.run();
} finally {
RequestContextHolder.resetRequestAttributes();
}
};
});
return executor;
}
}
//使用自定义的线程池
@Async(AppConfig.ASYNC_EXECUTOR_NAME)
public void asyncMethod(){
//do something
}
以上就定义了一个名为asyncExecutor的线程池,并设置了其corePoolSize、maxPoolSize、线程名称等相关的参数。在实际使用时还会遇到线程上下文切换的问题,需要对属性值进行复制:
public class ContextCopyingDecorator implements TaskDecorator {
public Runnable decorate(final Runnable runnable) {
final RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
return () -> {
try {
RequestContextHolder.setRequestAttributes(attributes);
runnable.run();
} finally {
RequestContextHolder.resetRequestAttributes();
}
};
}
}
总结:@Async是Spring中定义异步任务经常使用的注解,实际使用过程中通常会自定义其线程池,设置单独的线程名称以更方便地排查问题。同时在Request请求过程中线程切换,需要对相关属性值进行复制。