spring Async

@Async

在Spring中,我们可以使用@Async注释来使用异步。知道@Async的工作原理及其局限性,才能进行正确有效地使用。

@Async的工作原理

当在方法上添加Async注解,它会根据proxyTargetClass属性创建该对象的代理,其中定义了Async(JDK代理/ CGlib)。 然后,Spring尝试查找与上下文关联的线程池,以将此方法的逻辑作为单独的执行路径提交。确切地说,它搜索唯一的TaskExecutor bean或指定名字的Executor bean。如果找不到,则使用默认的SimpleAsyncTaskExecutor。

此外,需要在spring boot入口类 或者 异步方法所在的类 上配置 @EnableAsync 注解开启异步处理。

Executor bean

使用指定的线程池来执行异步任务:
创建一个线程池配置类 MyTaskConfigurer,并配置一个 任务线程池对象myTaskExecutor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration
public class MyTaskConfigurer {
@Bean("myTaskExecutor")
public Executor getTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(200);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("myTaskExecutor-");
executor.setRejectedExecutionHandler(new CallerRunsPolicy());
return executor;
}
}

使用@Async("myTaskExecutor")指定异步任务使用的线程池。

注意事项

  1. 通过本地实例化,并进行调用的方式,无法触发异步机制。
  2. 不要在私有方法上使用Async注解,运行时无法对私有方法创建代理,因此将无法生效。
  3. 使用Async注解的方法和调用者caller方法不应该在同一个类中,在同一个类中进行内部调用时,是直接访问,不会通过代理。(Spring proxy self-invocation)

实践所得

环境:spring boot 2.1.3

  1. 使用注解@Async,不指定线程池。
    经测试,所使用的默认线程池大小为8,名字前缀为task-
  2. 当在程序中有创建Executor bean时,使用未指定线程池的注解@Async配置任务函数。当函数被调用时,执行线程只有一个,名字为scheduling-1

附件

  1. SimpleAsyncTaskExecutor
    不重用线程,每次都创建新的线程去执行任务。

  2. Spring Boot: Creating Asynchronous Methods Using @Async Annotation

  3. Effective Advice on Spring Async: Part 1