Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >阿里面试:说说@Async实现原理?

阿里面试:说说@Async实现原理?

原创
作者头像
磊哥
发布于 2024-07-05 09:45:15
发布于 2024-07-05 09:45:15
2210
举报

@Async 是 Spring 3.0 提供的一个注解,用于标识某类(下的公共方法)或某方法会执行异步调用。

接下来,我们来看下 @Async 的基本使用和实现原理。

1.基本使用

@Async 基本使用可以分为以下 3 步:

  1. 项目中开启异步支持import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication @EnableAsync public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } }1.2 创建异步方法创建异步方法是在需要异步执行的方法上添加 @Async 注解,这个方法一定是要放在被 IoC 容器管理的 Bean 中,只有被 IoC 管理的类才能实现异步调用,例如在带有 @Service 注解的类中创建异步方法:import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @Service public class AsyncService { @Async public void performAsyncTask() { // 这里放置需要异步执行的代码 System.out.println("异步任务正在执行,当前线程:" + Thread.currentThread().getName()); } }1.3 调用异步方法在其他类或方法中,通过注入这个服务类的实例来调用异步方法。注意,直接在同一个类内部调用不会触发异步行为,必须通过注入的实例调用,使用 new 创建的对象也不能进行异步方法调用,具体实现代码如下:import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class MyController { @Autowired private AsyncService asyncService; @GetMapping("/startAsync") public String startAsyncTask() { asyncService.performAsyncTask(); return "异步任务已启动"; } }2.实现原理简单来说,@Async 注解是由 AOP(面向切面)实现的,具体来说,它是由 AsyncAnnotationAdvisor 这个切面类来实现的。
  2. 创建异步方法
  3. 调用异步方法1.1 开启异步支持以 Spring Boot 项目为例,我们首先需要在 Spring Boot 的启动类,也就是带有@SpringBootApplication 注解的类上添加 @EnableAsync 注解,以开启异步方法执行的支持,如下代码所示:

在 AsyncAnnotationAdvisor 中,会使用 AsyncExecutionInterceptor 来处理 @Async 注解,它会在被 @Async 注解标识的方法被调用时,创建一个异步代理对象来执行方法。这个异步代理对象会在一个新的线程中调用被 @Async 注解标识的方法,从而实现方法的异步执行。

在 AsyncExecutionInterceptor 中,核心方法是 getDefaultExecutor 方法,使用此方法来获取一个线程池来执行被 @Async 注解修饰的方法,它的实现源码如下:

代码语言:java
AI代码解释
复制
@Nullable
protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) {
    Executor defaultExecutor = super.getDefaultExecutor(beanFactory);
    return (Executor)(defaultExecutor != null ? defaultExecutor : new SimpleAsyncTaskExecutor());
}

此方法实现比较简单,它是先尝试调用父类 AsyncExecutionAspectSupport#getDefaultExecutor 方法获取线程池,如果父类方法获取不到线程池再用创建 SimpleAsyncTaskExecutor 对象作为 Async 的线程池返回。

而 SimpleAsyncTaskExecutor 中在执行任务时是这样的:

代码语言:java
AI代码解释
复制
protected void doExecute(Runnable task) {
    this.newThread(task).start();
}

可以看出,在 Spring 框架中如果使用默认的 @Async 注解,它的执行比较简单粗暴,并没有使用线程池,而是每次创建线程来执行,所以在 Spring 框架中是不能直接使用 @Async 注解的,需要使用 @Async 注解搭配自定义的线程池,既实现 AsyncConfigurer 接口来提供自定义的 ThreadPoolTaskExecutor 来创建线程池,以确保 @Async 能真正的使用线程池来执行异步任务。

然而,在 Spring Boot 中,因为在框架启动时,自动注入了 ThreadPoolTaskExecutor,如下源码所示:

代码语言:java
AI代码解释
复制
@ConditionalOnClass({ThreadPoolTaskExecutor.class})
@AutoConfiguration
@EnableConfigurationProperties({TaskExecutionProperties.class})
@Import({TaskExecutorConfigurations.ThreadPoolTaskExecutorBuilderConfiguration.class, TaskExecutorConfigurations.TaskExecutorBuilderConfiguration.class, TaskExecutorConfigurations.SimpleAsyncTaskExecutorBuilderConfiguration.class, TaskExecutorConfigurations.TaskExecutorConfiguration.class})
public class TaskExecutionAutoConfiguration {
    public static final String APPLICATION_TASK_EXECUTOR_BEAN_NAME = "applicationTaskExecutor";

    public TaskExecutionAutoConfiguration() {
    }
}

具体的构建细节源码如下:

代码语言:java
AI代码解释
复制
@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 注解,无需担心它每次都会创建线程来执行的问题

课后思考

为什么使用 @Async 注解不能解决循环依赖的问题?为什么使用 @Async 注解会导致事务实现?

本文已收录到我的面试小站 www.javacn.site,其中包含的内容有:Redis、JVM、并发、并发、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、设计模式、消息队列等模块。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Spring异步核心@Async注解的前世今生
可以很明显的发现,它使用的是线程池SimpleAsyncTaskExecutor,这也是Spring默认给我们提供的线程池(其实它不是一个真正的线程池,后面会有讲述)。下面原理部分讲解后,你就能知道怎么让它使用我们自定义的线程池了。
大忽悠爱学习
2022/09/29
1.3K0
Spring异步核心@Async注解的前世今生
SpringBoot线程池开发最佳实践!
即ThreadPoolTaskExecutor,但SpringBoot不同版本有区别。
JavaEdge
2025/06/01
3870
SpringBoot线程池开发最佳实践!
Spring Boot(5) @Async异步线程池详解
在Spring4中,Spring中引入了一个新的注解@Async,这个注解让我们在使用Spring完成异步操作变得非常方便。
黄规速
2022/04/14
5.7K0
Spring Boot(5) @Async异步线程池详解
不看绝对后悔的@Async深度解析【不仅仅是源码那么简单】
在整理老的业务逻辑代码时候发现好多接口实现上面都标记了 @Async注解。我本身对这个注解使用的比较少,异步逻辑我都习惯自定义ThreadPoolExecutor工具类。正好借着这次梳理代码结构,来看看 @Async这个注解到底在玩什么?
柏炎
2022/08/23
1.9K0
不看绝对后悔的@Async深度解析【不仅仅是源码那么简单】
springboot之线程池ThreadPoolTaskExecutor以及@Async异步注解
最近项目当中有需求,要进行异步的处理,需要使用到线程池,很久没有使用到线程池了,一来是做JAVAweb开发基本上很少用到异步处理,二来是发现有的老项目里面,线程和线程池的使用比较混乱,有好几个线程池,有的线程池是通过spring管理的,有的是自己创建的,然后有的地方是直接创建的线程。所以这里记录下自己在项目当中如何优雅的使用线程池!避免项目当中到处都是线程池!!!
海加尔金鹰
2020/06/08
36.8K0
Spring 异步实现原理与实战分享
最近因为全链路压测项目需要对用户自定义线程池 Bean 进行适配工作,我们知道全链路压测的核心思想是对流量压测进行标记,因此我们需要给压测的流量请求进行打标,并在链路中进行传递,那么问题来了,如果项目中使用了多线程处理业务,就会造成父子线程间无法传递压测打标数据,不过可以利用阿里开源的 ttl 解决这个问题。
张乘辉
2020/06/19
8430
Async的线程池使用的哪个?
在Spring中我们经常会用到异步操作,注解中使用 @EnableAsync 和 @Async 就可以使用它了。但是最近发现在异步中线程号使用的是我们项目中自定义的线程池 ThreadPoolTaskExecutor 而不是之前熟悉的 SimpleAsyncTaskExecutor
青衫染红尘
2022/08/02
1.3K0
你知道 @Async 是怎么让方法异步执行的吗?
@Async 是通过注解标记来开启方法的异步执行的;对于注解的底层实现,除了 java 原生提供那种依赖编译期植入的之外,其他的基本都差不多,即运行时通过反射等方式拦截到打了注解的类或者方法,然后执行时进行横切拦截;另外这里还有一个点就是方法异步执行,所以对于 @Async 的剖析,就一定绕不开两个基本的知识点,就是代理和线程池。 在了解到这些之后,我们来拆解下 @Async 的基本原理。
科技新语
2022/12/06
1K0
你知道 @Async 是怎么让方法异步执行的吗?
强烈反对使用Spring封装的多线程类!
在很久很久之前,我有一段痛苦的记忆。那种被故障所驱使的感觉,在我脑海里久久无法驱散。
xjjdog
2022/04/06
3500
强烈反对使用Spring封装的多线程类!
异步编程 - 08 Spring框架中的异步执行_TaskExecutor接口和@Async应用篇
在Spring Framework中分别使用TaskExecutor和TaskScheduler接口提供异步执行和任务调度的抽象。
小小工匠
2023/09/09
2.1K0
异步编程 - 08 Spring框架中的异步执行_TaskExecutor接口和@Async应用篇
关于Spring中的@Async注解以及为什么不建议使用 - Java技术债务
Async 注解是 Java 8 中的一个注解,用于标识一个方法是异步执行的。当一个方法被标记为 Async 时,该方法将在一个新的线程中执行,并且可以立即返回一个 CompletableFuture 对象。使用 CompletableFuture 可以更轻松地管理异步计算的结果。下面是一个使用 Async 注解的示例代码:
Java技术债务
2024/06/21
4720
Spring源码之Async注解
实现了 BeanFactoryAware 接口,初始化 AsyncAnnotationBeanPostProcessor 时会调用内部的**setBeanFactory() **方法设置切面
全栈程序员站长
2022/09/18
5260
Spring源码之Async注解
Spring中的多线程魔法:探索@Async注解的妙用
异步编程是一种编程模式,允许应用程序在执行某个操作的同时执行其他任务,而不必等待该操作完成。这提高了应用程序的性能和响应速度,特别是在需要执行长时间操作(例如网络请求或数据库查询)时。
一只牛博
2025/05/30
2630
线上问题-关于@Async
线程数统计:32278 个SimpleAsyncTaskExecutor,开了好多线程啊! 应该是线程太多
温安适
2022/01/10
5770
【小家Spring】Spring异步处理@Async的使用以及原理、源码分析(@EnableAsync)
在开发过程中,我们会遇到很多使用线程池的业务场景,例如异步短信通知、异步记录操作日志。大多数使用线程池的场景,就是会将一些可以进行异步操作的业务放在线程池中去完成。
YourBatman
2019/09/03
7.3K1
【小家Spring】Spring异步处理@Async的使用以及原理、源码分析(@EnableAsync)
新手也能看懂的 SpringBoot 异步编程指南
异步编程在处理耗时操作以及多任务处理的场景下非常有用,我们可以更好的让我们的系统利用好机器的 CPU 和 内存,提高它们的利用率。多线程设计模式有很多种,Future模式是多线程开发中非常常见的一种设计模式,本文也是基于这种模式来说明 SpringBoot 对于异步编程的知识。
Guide哥
2020/05/08
1.4K0
深入浅出Spring的@Async异步执行机制
在现代应用开发中,响应速度和吞吐量是衡量系统性能的关键指标。当面对耗时操作时,传统的同步执行方式会导致请求线程被阻塞,严重影响系统整体性能。Spring框架提供的异步执行机制正是为了解决这一痛点而生。
用户6320865
2025/08/27
1400
深入浅出Spring的@Async异步执行机制
Spring Boot启用异步线程
Spring中存在一个接口AsyncConfigurer接口,该接口就是用来配置异步线程池的接口,它有两个方法,getAsyncExecutor和getAsyncUncaughtExceptionHandler,第一个方法是获取一个线程池,第二个方法是用来处理异步线程中发生的异常。它的源码如下所示:
itlemon
2020/04/03
2.4K0
Spring 异步调用,一行代码实现!舒服,不接受任何反驳~
在日常开发中,我们的逻辑都是同步调用,顺序执行。在一些场景下,我们会希望异步调用,将和主线程关联度低的逻辑异步调用,以实现让主线程更快的执行完成,提升性能。例如说:记录用户访问日志到数据库,记录管理员操作日志到数据库中。
芋道源码
2020/05/25
6.1K0
Springboot使用线程池demo
创建一个线程池的配置,让Spring Boot加载,使用@Configuration和@EnableAsync这两个注解,表示这是线程池配置类
高大北
2022/06/14
7410
推荐阅读
相关推荐
Spring异步核心@Async注解的前世今生
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档