前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >阿里面试:说说@Async实现原理?

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

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

@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 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
快速学习-DataNode
1)一个数据块在DataNode上以文件形式存储在磁盘上,包括两个文件,一个是数据本身,一个是元数据包括数据块的长度,块数据的校验和,以及时间戳。 2)DataNode启动后向NameNode注册,通过后,周期性(1小时)的向NameNode上报所有的块信息。 3)心跳是每3秒一次,心跳返回结果带有NameNode给该DataNode的命令如复制块数据到另一台机器,或删除某个数据块。如果超过10分钟没有收到某个DataNode的心跳,则认为该节点不可用。 4)集群运行中可以安全加入和退出一些机器。
cwl_java
2020/02/19
6520
HDFS服役新数据节点与退役旧数据节点
随着公司业务的增长,数据量越来越大,原有的数据节点的容量已经不能满足存储数据的需求,需要在原有集群基础上动态添加新的数据节点。
用户4870038
2021/02/05
7690
HDFS服役新数据节点与退役旧数据节点
HDFS-集群扩容及缩容
白名单:在白名单的主机IP地址可以访问集群,对集群进行数据的存储。不在白名单的主机可以访问集群,但是不会在主机上存储数据 企业中:配置白名单,可以尽量防止黑客恶意访问攻击。
用户4283147
2022/10/27
1.1K0
HDFS-集群扩容及缩容
大数据技术之_04_Hadoop学习_02_HDFS_DataNode(面试开发重点)+HDFS 2.X新特性
思考:如果电脑磁盘里面存储的数据是控制高铁信号灯的红灯信号(1)和绿灯信号(0),但是存储该数据的磁盘坏了,一直显示是绿灯,是否很危险?同理DataNode节点上的数据损坏了,却没有发现,是否也很危险,那么如何解决呢?   如下是DataNode节点保证数据完整性的方法。
黑泽君
2019/03/04
7010
大数据技术之_04_Hadoop学习_02_HDFS_DataNode(面试开发重点)+HDFS 2.X新特性
HDFS新增节点与删除节点(9)
随着公司业务的增长,数据量越来越大,原有的数据节点的容量已经不能满足存储数据的需求,需要在原有集群基础上动态添加新的数据节点。因此,本篇博客小菌为大家带来的是HDFS新增节点与删除节点的内容。
大数据梦想家
2021/01/22
2.6K0
HDFS新增节点与删除节点(9)
大数据之Hadoop企业级生产调优手册(上)
每个文件块大概占用 150byte,一台服务器 128G内存为例,能存储多少文件块呢?128 * 128 * 1024 * 1024 / 150Byte ≈ 9.1亿
大数据真好玩
2021/10/12
1.5K0
大数据之Hadoop企业级生产调优手册(上)
Hadoop框架:DataNode工作机制详解
DataNode上数据块以文件形式存储在磁盘上,包括两个文件,一个是数据本身,一个是数据块元数据包括长度、校验、时间戳;
知了一笑
2020/11/02
7880
Hadoop框架:DataNode工作机制详解
HDSF集群扩容
1、关闭防火墙 2、关闭selinux 3、修改主机名 4、主机名与IP的对应关系 5、SSH免密码登录 6、安装JDK
云缓缓知我意
2021/04/15
3460
大数据成长之路-- hadoop集群的部署(4)
在namenod的cd /export/servers/hadoop-2.6.0-cdh5.14.0/etc/hadoop目录下创建dfs.hosts.exclude文件,并添加需要退役的主机名称 node01执行以下命令
Maynor
2021/04/09
5070
hadoop学习随笔-入门阶段
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-22uhcfxh-1617677655640)(D:\Code_Study\博客笔记\Hadoop学习笔记.assets\1604300154815.png)]
用户8483969
2021/04/09
5350
必须掌握的HDFS相关问题
安全模式是Namenode的一种状态(Namenode主要有active/standby/safemode三种模式)。
大数据学习与分享
2020/07/26
1K0
大数据成长之路-- hadoop集群的部署(3)
第六步:node04服务器关机重启并生成公钥与私钥 node04执行以下命令关机重启
Maynor
2021/04/09
3570
【史上最全】Hadoop 核心 - HDFS 分布式文件系统详解(上万字建议收藏)
Hadoop 分布式系统框架中,首要的基础功能就是文件系统,在 Hadoop 中使用 FileSystem 这个抽象类来表示我们的文件系统,这个抽象类下面有很多子实现类,究竟使用哪一种,需要看我们具体的实现类,在我们实际工作中,用到的最多的就是HDFS(分布式文件系统)以及LocalFileSystem(本地文件系统)了。
五分钟学大数据
2021/02/08
2.4K0
【史上最全】Hadoop 核心 - HDFS 分布式文件系统详解(上万字建议收藏)
HDFS集群缩容案例: Decommission DataNode
在HDFS集群运维过程中,常会碰到因为实际业务增长低于集群创建时的预估规模;集群数据迁出,数据节点冗余较多;费用控制等原因,需要对集群进行缩容操作。Decommission DataNode是该过程中关键的一步,就是把DataNode从集群中移除掉。那问题来了,HDFS在设计时就把诸如机器故障考虑进去了,能否直接把某台运行Datanode的机器关掉然后拔走呢?理论上可行的,不过在实际的集群中,如果某份数据只有一份副本而且它就在这个机器上,那么直接关掉并拔走机器就会造成数据丢失。本文将介绍如何Decommission DataNode以及可能会遇到Decommission超时案例及其解决方案。
EMR小助手
2020/10/23
6K0
Hadoop动态添加删除节点datanode及恢复
本文略去jdk安装,请将datanode的jdk安装路径与/etc/Hadoop/hadoop-evn.sh中的java_home保持一致,版本hadoop2.7.5
星哥玩云
2022/07/12
8980
[喵咪大数据]Hadoop节点添加下线和磁盘扩容操作
Hadoop绝非一个简单程序,集群模式下更是如此,所有的数据都存储在Hadoop中如果操作不当会存在丢失数据的风险,那么怎么在安全的情况,扩容下线维护或者磁盘满了怎么增加空间,就是今天的主要内容了. 附上: Hadoop的官网:hadoop.apache.org 喵了个咪的博客:w-blog.cn 1.增加节点 当磁盘满了或节点不够处理速度慢了都需要对节点或者通过增加节点的方式进行磁盘扩容,这个时候就需要用到Hadoop扩容机制了 通过如下命令可以查看各节点情况磁盘容量等 > hadoop dfsadm
喵了个咪233
2018/03/02
2.2K0
[喵咪大数据]Hadoop节点添加下线和磁盘扩容操作
hadoop-2:深入探索hadoop3.3.1集群模式下的各个组件
https://cwiki.apache.org/confluence/display/HADOOP/Hadoop+Java+Versions
千里行走
2021/08/12
1.9K0
hadoop-2:深入探索hadoop3.3.1集群模式下的各个组件
hdfs报错 'There are 10 missing blocks...'
今天本要部署spark on yarn,因为要在hdfs创建一些共享文件,于是到hdfs web UI去看了一下,结果发现下面的错误,顺手处理一下。
阿dai学长
2020/03/13
3.6K0
HDFS经典简答题(实习生必看!)
前一段时间,小菌陆续分享了HDFS系列1-12的博客,总算是要完结了。于是小菌打算再出一期关于HDFS的经典面试题,其中的内容大多都出自于在前面分享的博客中,感兴趣的小伙伴们可以自行浏览,链接小菌放到文末了哦~
大数据梦想家
2021/01/22
7200
HDFS经典简答题(实习生必看!)
Hadoop完全分布式集群搭建
用途栏中,也可以把namenode,secondaryNamenode及jobTracker
星哥玩云
2022/06/30
4520
相关推荐
快速学习-DataNode
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档