background
Will SpringBoot's @Async default thread pool cause OOM problems?
code:
@Component
public class AsyncService {
/**
* 异步方法
*/
@Async
public void test() {
System.out.println("AsyncService----Thread.currentThread().getName() = " + Thread.currentThread().getName());
}
}
Test class:
@Test
public void testAsync() {
for (int i = 0; i < 1000; i++) {
asyncService.test();
}
System.out.println("1.Thread.currentThread().getName() = " + Thread.currentThread().getName());
}
explore 1
environment
- Spring Boot version: 2.0.9.RELEASE
- @EnableAsync enables asynchronous
operation result
From the results, the number of threads is increasing
Principle exploration
Debug debugging, determine the Executor bean in the class AsyncExecutionAspectSupport
Here will get the default Executor, continue to go inside:
Here you will find that the default thread pool is SimpleAsyncTaskExecutor, and the principle of the execution method is as follows:
It can be seen from here that each task will create a thread. If there are too many tasks, OOM will definitely appear as a result.
Improve
Add a custom thread pool as the default thread pool of @Async, the above code:
// 这里做一个简单的测试,具体的可以通过配置来灵活设置这些值
@Configuration
public class MyExecutorConfig {
@Bean("myExecutor")
public Executor getMyExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(1000);
executor.setThreadNamePrefix("mytask-");
executor.initialize();
return executor;
}
}
The asynchronous method is modified as follows:@Async(“myExecutor”)
@Component
public class AsyncService {
@Async("myExecutor")
public void test() {
System.out.println("AsyncService----Thread.currentThread().getName() = " + Thread.currentThread().getName());
}
}
operation result:
The result is what we expected.
Why add our own defined bean here, and then go with our own definition. Handled by the AnnotationAsyncExecutionInterceptor class
@Override
@Nullable
protected String getExecutorQualifier(Method method) {
// Maintainer's note: changes made here should also be made in
// AnnotationAsyncExecutionAspect#getExecutorQualifier
Async async = AnnotatedElementUtils.findMergedAnnotation(method, Async.class);
if (async == null) {
async = AnnotatedElementUtils.findMergedAnnotation(method.getDeclaringClass(), Async.class);
}
return (async != null ? async.value() : null);
}
Inquiry 2
environment:
- Spring Boot version: 2.1.2.RELEASE
- @EnableAsync enables asynchronous
operation result
From the results, the number of threads is controlled within 10
Principle exploration
Debug debugging, determine the Executor bean in the class AsyncExecutionAspectSupport
Here will get the default Executor, continue to go inside:
Here you will find that the default thread pool is ThreadPoolTaskExecutor, which is no longer SimpleAsyncTaskExecutor . Here you can see that spring has improved this problem and no longer creates a thread for each task.
How is ThreadPoolTaskExecutor injected?
Spring has added an automatic configuration class TaskExecutionAutoConfiguration since version 2.1.0,
@Lazy
@Bean(name = APPLICATION_TASK_EXECUTOR_BEAN_NAME)
@ConditionalOnMissingBean(Executor.class)
public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
return builder.build();
}
This method is the definition of the ThreadPoolTaskExecutor bean
Summarize
1. Before Spring Boot version 2.1.0, using @Async will use the SimpleAsyncTaskExecutor thread pool by default when the Executor is not specified, and the large amount of tasks will cause OOM; this problem can be avoided after specifying the Executor
2. After Spring Boot version 2.1.0, using @Async will use ThreadPoolTaskExecutor by default to avoid OOM
3. @Async can be used, be careful when using it, it is best to specify a thread pool! ! !