Async注解剖析:@Async是否会导致OOM问题?

背景

SpringBoot的@Async默认线程池是否会导致OOM问题?

代码:

@Component
public class AsyncService {
    
    

    /**
   	* 异步方法
   	*/
    @Async
    public void test() {
    
    
        System.out.println("AsyncService----Thread.currentThread().getName() = " + Thread.currentThread().getName());
    }
}

测试类:

@Test
public void testAsync() {
    
    
    for (int i = 0; i < 1000; i++) {
    
    

        asyncService.test();
    }
    System.out.println("1.Thread.currentThread().getName() = " + Thread.currentThread().getName());
}

探究1

环境

  • Spring Boot的版本: 2.0.9.RELEASE
  • @EnableAsync开启异步

运行结果

在这里插入图片描述

从结果看,线程数在不断增加

原理探究

debug调试,在类AsyncExecutionAspectSupport中确定Executor bean

在这里插入图片描述

这里会获取默认的Executor,继续往里走:

在这里插入图片描述

这里会发现默认的线程池是SimpleAsyncTaskExecutor,执行方法的原理如下:

在这里插入图片描述

从这里可以看到,每一个任务就会创建一个线程,如果任务多了,结果可想而知肯定会出现OOM

改进

增加一个自定义的线程池,作为@Async的默认线程池,上代码:

// 这里做一个简单的测试,具体的可以通过配置来灵活设置这些值
@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;
    }
}

异步方法作如下修改:@Async(“myExecutor”)

@Component
public class AsyncService {
    
    

    @Async("myExecutor")
    public void test() {
    
    
        System.out.println("AsyncService----Thread.currentThread().getName() = " + Thread.currentThread().getName());
    }
}

运行结果:

在这里插入图片描述

结果是我们预期的。

这里为什么加上我们自己定义的bean之后,就走我们自己定义的了呢。AnnotationAsyncExecutionInterceptor类处理的

@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);
}

探究2

环境:

  • Spring Boot的版本: 2.1.2.RELEASE
  • @EnableAsync开启异步

运行结果

在这里插入图片描述

从结果看,线程数控制在10以内

原理探究

debug调试,在类AsyncExecutionAspectSupport中确定Executor bean

在这里插入图片描述

这里会获取默认的Executor,继续往里走:

在这里插入图片描述

这里会发现默认的线程池是ThreadPoolTaskExecutor,不再是SimpleAsyncTaskExecutor了,这里可以看出spring对这块存在的问题进行了改进,不再对每个任务再去创建一个线程

ThreadPoolTaskExecutor是如何注入的呢?

在这里插入图片描述

spring自2.1.0版本加入了一个自动配置类TaskExecutionAutoConfiguration,

@Lazy
@Bean(name = APPLICATION_TASK_EXECUTOR_BEAN_NAME)
@ConditionalOnMissingBean(Executor.class)
public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
    
    
   return builder.build();
}

此方法即是ThreadPoolTaskExecutor bean的定义

总结

1. Spring Boot在2.1.0版本之前, 使用@Async 在不指定 Executor时 会默认使用 SimpleAsyncTaskExecutor线程池,任务量大会导致OOM;在指定Executor后可以避免此问题

2. Spring Boot在2.1.0版本之后, 使用@Async 会默认使用ThreadPoolTaskExecutor,避免OOM

3. @Async是可以使用的,使用时须慎重,最好指定线程池!!!

猜你喜欢

转载自blog.csdn.net/Tiny_Cao/article/details/128490893