浅析Spring中Async注解底层异步线程池原理
一、前言
开发中我们经常会用到异步方法调用,具体到代码层面,异步方法调用的实现方式有很多种,比如最原始的通过实现Runnable接口或者继承Thread类创建异步线程,然后启动异步线程;再如,可以直接用java.util.concurrent包提供的线程池相关API实现异步方法调用。
如果说可以用一行代码快速实现异步方法调用,那是不是比上面方法香很多。
Spring提供了Async注解,就可以帮助我们一行代码搞定异步方法调用。Async注解用起来是很爽,但是如果不对其底层实现做深入研究,难免有时候也会心生疑虑,甚至会因使用不当,遇见一些让人摸不着头脑的问题。
本文首先将对Async注解做简单介绍,然后和大家分享一个我们项目中因Async注解使用不当的线上问题,接着再深扒Spring源码,对Async注解底层异步线程池的实现原理一探究竟。
二、Async注解简介
Async注解定义源码

从源码可以看出@Async注解定义很简单,只需要关注两点:
-
Target({ElementType.TYPE, ElementType.METHOD})标志Async注解可以作用在方法和类上,作用在类上时,类的所有方法可以实现异步调用。
-
String value( ) default ""是唯一字段属性,用来指定异步线程池,且该字段有缺省值。
Async注解异步调用实现原理概述
在Spring框架中,Async注解的实现是通过AOP来实现的。具体来说,Async注解是由AsyncAnnotationAdvisor这个切面类来实现的。
AsyncAnnotationAdvisor类是Spring框架中用于处理Async注解的切面,它会在被Async注解标识的方法被调用时,创建一个异步代理对象来执行方法。这个异步代理对象会在一个新的线程中调用被@Async注解标识的方法,从而实现方法的异步执行。
在AsyncAnnotationAdvisor中,会使用AsyncExecutionInterceptor来处理Async注解。AsyncExecutionInterceptor是实现了MethodInterceptor接口的类,用于拦截被Async注解标识的方法的调用,并在一个新的线程中执行这个方法。
通过AOP的方式实现Async注解的异步执行,Spring框架可以在方法调用时动态地创建代理对象来实现异步执行,而不需要在业务代码中显式地创建新线程。
总的来说,Async注解的实现是通过AOP机制来实现的,具体的切面类是AsyncAnnotationAdvisor,它利用AsyncExecutionInterceptor来处理被Async注解标识的方法的调用,实现方法的异步执行。
三、Async注解底层异步线程池原理探究
获取Async注解线程池主流程解析
进入到Spring源码Async注解AOP切面实现部分,我们重点剖析异步调用实现中线程池是怎么处理的。下图是org.springframework.aop.interceptor.AsyncExecutionInterceptor#invoke方法的实现,可以看出是调用determineAsyncExecutor方法获取异步线程池。

AsyncExecutionInterceptor#invoke
下图是determineAsyncExecutor方法实现:


上方的图为AsyncExecutionInterceptor#determineAsyncExecutor,下方的图为AsyncExecutionAspectSupport#getExecutorQualifier
从代码实现中可以看到determineAsyncExecutor获取线程池的大致流程:

determineAsyncExecutor获取线程池流程
如果在使用Async注解时指定了自定义线程池比较好理解,如果使用Async注解时没有指定自定义线程池,Spring是怎么处理默认线程池呢?继续深入源码看看Spring提供的默认线程池的实现。
Spring是怎么为Async注解提供默认线程池的
Async注解默认线程池有下面两个方法实现:
-
org.springframework.aop.interceptor.AsyncExecutionInterceptor#getDefaultExecutor
-
org.springframework.aop.interceptor.AsyncExecutionAspectSupport#getDefaultExecutor

AsyncExecutionInterceptor#getDefaultExecutor
可以看出AsyncExecutionInterceptor#getDefaultExecutor方法比较简单:先尝试调用父类AsyncExecutionAspectSupport#getDefaultExecutor方法获取线程池,如果父类方法获取不到线程池再用创建SimpleAsyncTaskExecutor对象作为Async的线程池返回。

AsyncExecutionAspectSupport#getDefaultExecutor
再来看父类AsyncExecutionAspectSupport#getDefaultExecutor方法的实现,可以看到Spring根据类型从Spring容器中获取TaskExecutor类的实例,先记住这个关键点。
我们知道,Spring根据类型获取实例时,如果spring容器中有且只有一个指定类型的实例对象,会直接返回,否则的话,会抛出NoUniqueBeanDefinitionException异常或者NoSuchBeanDefinitionException异常。
但是,对于Executor类型,Spring容器却“网开一面”,有一个特殊处理:当从Spring容器中获取Executor实例对象时,如果满足@ConditionalOnMissingBean(Executor.class)条件,Spring容器会自动装载一个ThreadPoolTaskExecutor实例对象,而ThreadPoolTaskExecutor是TaskExecutor的实现类。


上方的图为TaskExecutionAutoConfiguration,下方的图为TaskExecutionProperties
从TaskExecutionProperties和TaskExecutionAutoConfiguration两个配置类我们看到Spring自动装载的ThreadPoolTaskExecutor线程池对象的参数:核心线程数=8;最大线程数=Integer.MAX_VALUE;队列大小=Integer.MAX_VALUE。
四、总结
现在Async注解线程池源码已经看的差不多了,下面这张图是Spring处理Async异步线程池的流程:

Async异步线程池获取流程
归纳一下:如果在使用Async注解时没有指定自定义的线程池会出现以下几种情况:
-
当Spring容器中有且仅有一个TaskExecutor实例时,Spring会用这个线程池来处理Async注解的异步任务,这可能会踩坑,如果这个TaskExecutor实例是第三方jar引入的,可能会出现很诡异的问题。
-
Spring创建一个核心线程数=8、最大线程数=Integer.MAX_VALUE、队列大小=Integer.MAX_VALUE的线程池来处理Async注解的异步任务,这时候也可能会踩坑,由于线程池参数设置不合理,核心线程数=8,队列大小过大,如果有大批量并发任务,可能会出现OOM。
-
Spring创建SimpleAsyncTaskExecutor实例来处理Async注解的异步任务,SimpleAsyncTaskExecutor不是一个好的线程池实现类,SimpleAsyncTaskExecutor根据需要在当前线程或者新线程中执行异步任务。如果当前线程已经有空闲线程可用,任务将在当前线程中执行,否则将创建一个新线程来执行任务。由于这个线程池没有线程管理的能力,每次提交任务都实时创建新城,所以如果任务量大,会导致性能下降。
*文/Simon
本文属得物技术原创,更多精彩文章请看:得物技术
未经得物技术许可严禁转载,否则依法追究法律责任!
相关文章:
浅析Spring中Async注解底层异步线程池原理
一、前言 开发中我们经常会用到异步方法调用,具体到代码层面,异步方法调用的实现方式有很多种,比如最原始的通过实现Runnable接口或者继承Thread类创建异步线程,然后启动异步线程;再如,可以直接用java.uti…...
sqli-labs 靶场 less-7 第七关详解:OUTFILE注入与配置
SQLi-Labs是一个用于学习和练习SQL注入漏洞的开源应用程序。通过它,我们可以学习如何识别和利用不同类型的SQL注入漏洞,并了解如何修复和防范这些漏洞。Less 7 SQLI DUMB SERIES-7判断注入点 进入页面中,并输入数据查看结果。 发现空数据提…...
AIGC新秀亮相,哪款大模型产品最得你心?
AIGC新秀亮相,哪款大模型产品最得你心? 近日,腾讯元宝APP的正式上线,为国内大模型AIGC产品市场增添了一名新成员。 这些所谓的“全能”大模型产品,凭借其强大的生成能力和广泛的应用场景,正逐渐改变我们的…...
RabbitMQ消息的可靠传输和防止消息丢失
在Spring Cloud项目中,为了确保RabbitMQ消息的可靠传输和防止消息丢失,需要考虑以下几个方面: 消息持久化:确保消息在RabbitMQ中持久化。队列持久化:确保队列是持久化的。发布确认:使用发布确认机制确保消…...
.net8系列-图文并茂手把手教你使用Nlog记录.net日志
Nlog是什么? NLog是一个为.NET平台设计的灵活且免费的日志记录库。它适用于包括.NET Framework、.NET Core和.NET Standard在内的多种.NET环境。 Nlog有什么好处或者特点? 配置灵活:NLog允许开发者通过配置文件(通常是NLog.conf…...
课时158:脚本发布_简单脚本_远程执行
2.1.3 远程执行 学习目标 这一节,我们从 基础知识、简单实践、小结 三个方面来学习 基础知识 简介 有时候,我们需要通过远程方式到另外一台主机进行脚本的执行 格式:ssh 远程主机登录用户名远程主机ip地址 "执行命令"效果 [r…...
3dmax2025能用云渲染吗?2025最新云渲染渲染100使用方法
3dmax2025还没用上云渲染?简单3步用上云渲染。 第一步,打开浏览器搜索渲染100,并进入下载客户端并安装 第二步,打开已安装的客户端进行安装,点击登录,未登录注册个账号即可(注册账号时邀请码填…...
从零开始学GeoServer源码(一)(搭建开发环境Win10+IDEA23.3.5+jdk11+geoserver2.24.x)
搭建开发环境 参考资料 0、基础环境准备0.1、idea0.2、jdk0.3、源码 1、导入工程2、配置启动环境2.1、打开新增配置面板2.2、配置工作目录2.2.1、从常用配置中选择2.2.2、直接粘贴 2.3最终效果 3、调整源码3.1、添加maven引用3.2、注释无效代码3.3、删除测试代码 4、修改运行端…...
分类模型:MATLAB判别分析
1. 判别分析简介 判别分析(Discriminant Analysis) 是一种统计方法,用于在已知分类的样本中构建分类器,并根据特征变量对未知类别的样本进行分类。常见的判别分析方法包括线性判别分析(Linear Discriminant Analysis, …...
生产 的mybatisplus 日志输入到日志文件
默认是输出到控制台.不输出到日志文件 输出到日志文件.需要修改配置 第一步. logging:config: classpath:logback-wshoto.xml第二步 mybatis-plus:configuration:cache-enabled: truedefault-executor-type: reuselog-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl第三步…...
八分钟生成一篇两万字的文章演示——《基于灰色预测的人口预测模型》
文章目录 工具使用 《基于灰色预测的人口预测模型》-全文由AI一次性生成文献综述研究方法模型开发灰色预测模型的数学构建参数估计模型验证 案例研究案例研究描述数据收集与预处理灰色预测模型的应用 文献综述研究方法模型开发灰色预测模型的数学构建参数估计模型验证 案例研究…...
golang 透明底图转白底
url : ""var s []byte// 请求线上图片s GetUrl(url)// 处理透明底图转白底img, _, err : image.Decode(bytes.NewReader(s))if err ! nil {fmt.Println("读取图片失败")}bounds : img.Bounds()dst : image.NewNRGBA(bounds)draw.Draw(dst, bounds, image.…...
【一】【网络使用小知识】使用aria2软件结合Windows PowerShell命令行快速下载文件
下载aria2软件 点击进入网址,aria2下载网址. 下载windows版本. 通过Windows PowerShell命令行使用aria2软件下载文件 通用下载文件命令行代码 aria2软件完整路径 -x 16 -s 32 -d 下载目录(文件夹) -o 文件名 下载链接路径示例,用aria2下载qq 找到aria2应用的直接地址,结合…...
报错:C1189#error: The <experimental/filesystem> header providing 解决方案
今天开发过程中,需要使用文件系统experimental/filesystem,报错C1189#error: The <experimental/filesystem> header providing ,通过以下解决方案,成功运行程序。 目录 一、打开项目下的属性 二、选择C/…...
Elixir学习笔记——速构(函数式编程基础)
在 Elixir 中,循环遍历 Enumerable 是很常见的,通常会过滤掉一些结果并将值映射到另一个列表中。 速构是此类构造的语法糖:它们将这些常见任务分组为 for 特殊形式。 例如,我们可以将一串整数映射到它们的平方值: 速构…...
开源项目大合集(热门)
人不走空 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌赋:斯是陋室,惟吾德馨 目录 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌…...
【JVM】JVisualVM的介绍、使用和GC过程
VisualVM介绍 VisualVM 是Netbeans的profile子项目,已在JDK6.0 update 7 中自带,能够监控线程,内存情况,查看方法的CPU时间和内存中的对 象,已被GC的对象,反向查看分配的堆栈(如100个String对象分别由哪几…...
个人在家如何获取World Scientific文献的经验分享
今天有位同学求助一篇World Scientific文献,他的学校虽然有这个数据库,但订购的该数据库资源内容有限,这位同学所需的文献不在学校订购范围内所以下载不了。今天小编就分享一个在家就可获取各个数据库文献的方法。本文以这篇求助文献为例&…...
Java 收集常见面试题
set和list的区别?给定一系列字符串,从集合的set和list中查询,如何查询出相关的数据? 在Java中,Set和List都是用于存储对象的集合 Set: 不允许包含重复的元素。 没有顺序(即不保证元素的迭代顺序…...
JS 严格模式和正常模式的区别
严格模式使用"use strict"; 作用: 消除 Javascript 语法的一些不合理、不严谨之处,减少一些怪异行为;消除代码运行的一些不安全之处,保证代码运行的安全;提高编译器效率,增加运行速度;为未来新…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...
YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...
华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...
学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
SpringTask-03.入门案例
一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...
