简介
Spring中用@Async注解标记的方法,称为异步方法,它会在调用方的当前线程之外的独立的线程中执行,其实就相当于我们自己new Thread(()-> System.out.println(“hello world !”))这样在另一个线程中去执行相应的业务逻辑。本篇先只讲@Async的使用,后面会分析它实现原理。
@Async注解使用条件:
- @Async注解一般用在类的方法上,如果用在类上,那么这个类所有的方法都是异步执行的;
- 所使用的@Async注解方法的类对象应该是Spring容器管理的bean对象;
- 调用异步方法类上需要配置上注解@EnableAsync(或者在SpringBoot中的配置类上)
使用注意:
1、默认情况下(即@EnableAsync注解的mode=AdviceMode.PROXY),同一个类内部没有使用@Async注解修饰的方法调用@Async注解修饰的方法,是不会异步执行的,这点跟 @Transitional 注解类似,底层都是通过动态代理实现的。如果想实现类内部自调用也可以异步,则需要切换@EnableAsync注解的mode=AdviceMode.ASPECTJ,详见@EnableAsync注解。
2、任意参数类型都是支持的,但是方法返回值必须是void或者Future类型。当使用Future时,你可以使用 实现了Future接口的ListenableFuture接口或者CompletableFuture类与异步任务做更好的交互。如果异步方法有返回值,没有使用Future类型的话,调用方获取不到返回值
关于有无返回值的使用可参考这个文章 SpringBoot异步任务, 以及带返回值的异步任务
@Async 源码
注解的源码如下:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Async {
/**
* A qualifier value for the specified asynchronous operation(s).
* <p>May be used to determine the target executor to be used when executing this
* method, matching the qualifier value (or the bean name) of a specific
* {@link java.util.concurrent.Executor Executor} or
* {@link org.springframework.core.task.TaskExecutor TaskExecutor}
* bean definition.
* (可以匹配自定义的任务执行器-->Executor类型或者TaskExecutor类型的bean)
* <p>When specified on a class level {@code @Async} annotation, indicates that the
* given executor should be used for all methods within the class. Method level use
* of {@code Async#value} always overrides any value set at the class level.
* (在方法上的设置会覆盖在类上的设置)
* @since 3.1.2
*/
String value() default "";
阅读@Async的源码以及其中涉及的Executor、TaskExecutor 等类,底层也是使用了线程池以及AOP技术。
@EnableAsync注解:
原理可参考这篇文章:@Async注解原理
使用详情
@Async异步方法默认使用Spring创建ThreadPoolTaskExecutor(参考TaskExecutionAutoConfiguration),
其中默认核心线程数为8, 默认最大队列和默认最大线程数都是Integer.MAX_VALUE. 创建新线程的条件是队列填满时, 而
这样的配置队列永远不会填满, 如果有@Async注解标注的方法长期占用线程(比如HTTP长连接等待获取结果),
在核心8个线程数占用满了之后, 新的调用就会进入队列, 外部表现为没有执行。
可以手动配置相应属性:
#核心线程数
spring.task.execution.pool.core-size=200
#最大线程数
spring.task.execution.pool.max-size=1000
#空闲线程保留时间
spring.task.execution.pool.keep-alive=3s
#队列容量
spring.task.execution.pool.queue-capacity=1000
#线程名称前缀
spring.task.execution.thread-name-prefix=test-thread-