Async Annotation Analysis: Does @Async Cause OOM Problems?

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

insert image description here

From the results, the number of threads is increasing

Principle exploration

Debug debugging, determine the Executor bean in the class AsyncExecutionAspectSupport

insert image description here

Here will get the default Executor, continue to go inside:

insert image description here

Here you will find that the default thread pool is SimpleAsyncTaskExecutor, and the principle of the execution method is as follows:

insert image description here

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:

insert image description here

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

insert image description here

From the results, the number of threads is controlled within 10

Principle exploration

Debug debugging, determine the Executor bean in the class AsyncExecutionAspectSupport

insert image description here

Here will get the default Executor, continue to go inside:

insert image description here

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?

insert image description here

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! ! !

Guess you like

Origin blog.csdn.net/Tiny_Cao/article/details/128490893