@Async 是 Spring 3.0 提供的一个注解,用于标识某类(下的公共方法)或某方法会执行异步调用。
接下来,我们来看下 @Async 的基本使用和实现原理。
@Async 基本使用可以分为以下 3 步:
项目中开启异步支持
创建异步方法
调用异步方法
以 Spring Boot 项目为例,我们首先需要在 Spring Boot 的启动类,也就是带有@SpringBootApplication 注解的类上添加 @EnableAsync 注解,以开启异步方法执行的支持,如下代码所示:
importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.scheduling.annotation.EnableAsync;@SpringBootApplication@EnableAsyncpublicclass MyApplication {publicstatic void main(String[]args){ SpringApplication.run(MyApplication.class,args);} }
创建异步方法是在需要异步执行的方法上添加 @Async 注解,这个方法一定是要放在被 IoC 容器管理的 Bean 中,只有被 IoC 管理的类才能实现异步调用,例如在带有 @Service 注解的类中创建异步方法:
importorg.springframework.scheduling.annotation.Async;importorg.springframework.stereotype.Service;@Servicepublicclass AsyncService {@Asyncpublicvoid performAsyncTask(){// 这里放置需要异步执行的代码System.out.println("异步任务正在执行,当前线程:"+Thread.currentThread().getName());} }
在其他类或方法中,通过注入这个服务类的实例来调用异步方法。注意,直接在同一个类内部调用不会触发异步行为,必须通过注入的实例调用,使用 new 创建的对象也不能进行异步方法调用,具体实现代码如下:
importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RestController;@RestControllerpublicclass MyController {@Autowiredprivate AsyncService asyncService;@GetMapping("/startAsync")publicString startAsyncTask(){ asyncService.performAsyncTask();return"异步任务已启动";} }
简单来说,@Async 注解是由 AOP(面向切面)实现的,具体来说,它是由 AsyncAnnotationAdvisor 这个切面类来实现的。
在 AsyncAnnotationAdvisor 中,会使用 AsyncExecutionInterceptor 来处理 @Async 注解,它会在被 @Async 注解标识的方法被调用时,创建一个异步代理对象来执行方法。这个异步代理对象会在一个新的线程中调用被 @Async 注解标识的方法,从而实现方法的异步执行。
在 AsyncExecutionInterceptor 中,核心方法是 getDefaultExecutor 方法,使用此方法来获取一个线程池来执行被 @Async 注解修饰的方法,它的实现源码如下:
@Nullableprotected Executor getDefaultExecutor(@NullableBeanFactory beanFactory){ Executor defaultExecutor=super.getDefaultExecutor(beanFactory);return(Executor)(defaultExecutor!=null? defaultExecutor : new SimpleAsyncTaskExecutor());}
此方法实现比较简单,它是先尝试调用父类 AsyncExecutionAspectSupport#getDefaultExecutor 方法获取线程池,如果父类方法获取不到线程池再用创建 SimpleAsyncTaskExecutor 对象作为 Async 的线程池返回。
而 SimpleAsyncTaskExecutor 中在执行任务时是这样的:
protected void doExecute(Runnable task){ this.newThread(task).start();}
可以看出,在 Spring 框架中如果使用默认的 @Async 注解,它的执行比较简单粗暴,并没有使用线程池,而是每次创建线程来执行,所以在 Spring 框架中是不能直接使用 @Async 注解的,需要使用 @Async 注解搭配自定义的线程池,既实现 AsyncConfigurer 接口来提供自定义的 ThreadPoolTaskExecutor 来创建线程池,以确保 @Async 能真正的使用线程池来执行异步任务。
然而,在 Spring Boot 中,因为在框架启动时,自动注入了 ThreadPoolTaskExecutor,如下源码所示:
@ConditionalOnClass({ThreadPoolTaskExecutor.class})@AutoConfiguration@EnableConfigurationProperties({TaskExecutionProperties.class})@Import({TaskExecutorConfigurations.ThreadPoolTaskExecutorBuilderConfiguration.class,TaskExecutorConfigurations.TaskExecutorBuilderConfiguration.class,TaskExecutorConfigurations.SimpleAsyncTaskExecutorBuilderConfiguration.class,TaskExecutorConfigurations.TaskExecutorConfiguration.class})publicclass TaskExecutionAutoConfiguration {publicstatic final String APPLICATION_TASK_EXECUTOR_BEAN_NAME="applicationTaskExecutor";publicTaskExecutionAutoConfiguration(){ } }
具体的构建细节源码如下:
@Bean @ConditionalOnMissingBean({TaskExecutorBuilder.class, ThreadPoolTaskExecutorBuilder.class}) ThreadPoolTaskExecutorBuilder threadPoolTaskExecutorBuilder(TaskExecutionProperties properties, ObjectProvider<ThreadPoolTaskExecutorCustomizer> threadPoolTaskExecutorCustomizers, ObjectProvider<TaskExecutorCustomizer> taskExecutorCustomizers, ObjectProvider<TaskDecorator> taskDecorator) { TaskExecutionProperties.Pool pool = properties.getPool(); ThreadPoolTaskExecutorBuilder builder = new ThreadPoolTaskExecutorBuilder(); builder = builder.queueCapacity(pool.getQueueCapacity()); builder = builder.corePoolSize(pool.getCoreSize()); builder = builder.maxPoolSize(pool.getMaxSize()); builder = builder.allowCoreThreadTimeOut(pool.isAllowCoreThreadTimeout()); builder = builder.keepAlive(pool.getKeepAlive()); TaskExecutionProperties.Shutdown shutdown = properties.getShutdown(); builder = builder.awaitTermination(shutdown.isAwaitTermination()); builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod()); builder = builder.threadNamePrefix(properties.getThreadNamePrefix()); Stream var10001 = threadPoolTaskExecutorCustomizers.orderedStream(); Objects.requireNonNull(var10001); builder = builder.customizers(var10001::iterator); builder = builder.taskDecorator((TaskDecorator)taskDecorator.getIfUnique()); builder = builder.additionalCustomizers(taskExecutorCustomizers.orderedStream().map(this::adapt).toList()); return builder; }
因此在 Spring Boot 框架中可以直接使用 @Async 注解,无需担心它每次都会创建线程来执行的问题。