news 2026/2/8 8:34:58

Async的线程池使用的哪个?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Async的线程池使用的哪个?

前言

在Spring中我们经常会用到异步操作,注解中使用@EnableAsync@Async就可以使用它了。但是最近发现在异步中线程号使用的是我们项目中自定义的线程池ThreadPoolTaskExecutor而不是之前熟悉的SimpleAsyncTaskExecutor

那么来看一下他的执行过程吧。

正文

  1. 首先要使异步生效,我们得在启动类中加入@EnableAsync那么就点开它看看。它会使用@Import注入一个AsyncConfigurationSelector类,启动是通过父类可以决定它使用的是配置类ProxyAsyncConfiguration
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> { private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME = "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration"; public AsyncConfigurationSelector() { } @Nullable public String[] selectImports(AdviceMode adviceMode) { switch(adviceMode) { case PROXY: return new String[]{ProxyAsyncConfiguration.class.getName()}; case ASPECTJ: return new String[]{"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration"}; default: return null; } } }
  1. 点开能够看到注入一个AsyncAnnotationBeanPostProcessor。它实现了BeanPostProcessor接口,因此它是一个后处理器,用于将Spring AOPAdvisor应用于给定的bean。从而该bean上通过异步注解所定义的方法在调用时会被真正地异步调用起来。
public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration { public ProxyAsyncConfiguration() { } @Bean( name = {"org.springframework.context.annotation.internalAsyncAnnotationProcessor"} ) @Role(2) public AsyncAnnotationBeanPostProcessor asyncAdvisor() { Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected"); AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor(); bpp.configure(this.executor, this.exceptionHandler); Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation"); if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) { bpp.setAsyncAnnotationType(customAsyncAnnotation); } bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass")); bpp.setOrder((Integer)this.enableAsync.getNumber("order")); return bpp; } }
  1. AsyncAnnotationBeanPostProcessor的父类实现了BeanFactoryAware,那么会在AsyncAnnotationBeanPostProcessor实例化之后回调setBeanFactory()来实例化切面AsyncAnnotationAdvisor
public void setBeanFactory(BeanFactory beanFactory) { super.setBeanFactory(beanFactory); //定义一个切面 AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler); if (this.asyncAnnotationType != null) { advisor.setAsyncAnnotationType(this.asyncAnnotationType); } advisor.setBeanFactory(beanFactory); this.advisor = advisor; }
  1. AsyncAnnotationAdvisor构造和声明切入的目标(切点)和代码增强(通知)。
public AsyncAnnotationAdvisor( @Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) { Set<Class<? extends Annotation>> asyncAnnotationTypes = new LinkedHashSet<>(2); asyncAnnotationTypes.add(Async.class); try { asyncAnnotationTypes.add((Class<? extends Annotation>) ClassUtils.forName("javax.ejb.Asynchronous", AsyncAnnotationAdvisor.class.getClassLoader())); } catch (ClassNotFoundException ex) { // If EJB 3.1 API not present, simply ignore. } //通知 this.advice = buildAdvice(executor, exceptionHandler); //切入点 this.pointcut = buildPointcut(asyncAnnotationTypes); }
  1. 通知就是最终要执行的。buildAdvice用于构建通知,主要是创建一个AnnotationAsyncExecutionInterceptor类型的拦截器,并且配置好使用的执行器和异常处理器。真正的异步执行的代码在AsyncExecutionAspectSupport中!
protected Advice buildAdvice( @Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) { AnnotationAsyncExecutionInterceptor interceptor = new AnnotationAsyncExecutionInterceptor(null); //配置拦截器 interceptor.configure(executor, exceptionHandler); return interceptor; }
  1. 配置拦截器,通过参数配置自定义的执行器和异常处理器或者使用默认的执行器和异常处理器。
public void configure(@Nullable Supplier<Executor> defaultExecutor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) { //默认执行器 this.defaultExecutor = new SingletonSupplier<>(defaultExecutor, () -> getDefaultExecutor(this.beanFactory)); this.exceptionHandler = new SingletonSupplier<>(exceptionHandler, SimpleAsyncUncaughtExceptionHandler::new); }
  1. getDefaultExecutor()方法,用来查找默认的执行器,父类AsyncExecutionAspectSupport首先寻找唯一一个类型为TaskExecutor的执行器并返回,若存在多个则寻找默认的执行器taskExecutor,若无法找到则直接返回null。子类AsyncExecutionInterceptor重写getDefaultExecutor方法,首先调用父类逻辑,返回null则配置一个名为SimpleAsyncTaskExecutor的执行器
/** * 父类 * 获取或构建此通知实例的默认执行器 * 这里返回的执行器将被缓存以供后续使用 * 默认实现搜索唯一的TaskExecutor的bean * 在上下文中,用于名为“taskExecutor”的Executor bean。 * 如果两者都不是可解析的,这个实现将返回 null */ @Nullable protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) { if (beanFactory != null) { try { // 搜索唯一的一个TaskExecutor类型的bean并返回 return beanFactory.getBean(TaskExecutor.class); } catch (NoUniqueBeanDefinitionException ex) { //找不到唯一一个bean异常后,搜索一个TaskExecutor类型的“taskExecutor”的bean并返回 logger.debug("Could not find unique TaskExecutor bean", ex); try { return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class); } catch (NoSuchBeanDefinitionException ex2) { if (logger.isInfoEnabled()) { logger.info("More than one TaskExecutor bean found within the context, and none is named " + "'taskExecutor'. Mark one of them as primary or name it 'taskExecutor' (possibly " + "as an alias) in order to use it for async processing: " + ex.getBeanNamesFound()); } } } catch (NoSuchBeanDefinitionException ex) { //未找到异常时搜索一个TaskExecutor类型的“taskExecutor”的bean并返回 logger.debug("Could not find default TaskExecutor bean", ex); try { return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class); } catch (NoSuchBeanDefinitionException ex2) { logger.info("No task executor bean found for async processing: " + "no bean of type TaskExecutor and no bean named 'taskExecutor' either"); } // Giving up -> either using local default executor or none at all... } } return null; } /** * 子类 * 如父类为null则重新实例化一个名为SimpleAsyncTaskExecutor的执行器 */ @Override @Nullable protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) { Executor defaultExecutor = super.getDefaultExecutor(beanFactory); return (defaultExecutor != null ? defaultExecutor : new SimpleAsyncTaskExecutor()); }

所以,到了这一步就可以理解为什么异步线程名默认叫SimpleAsyncTaskExecutor-xx,为什么有了自己的线程池有可能异步用到了自己的线程池配置。

我们有这个切入点之后,每次请求接口执行异步方法前都会执行AsyncExecutionInterceptor#invoke()determineAsyncExecutor用来决策使用哪个执行器

@Nullable protected AsyncTaskExecutor determineAsyncExecutor(Method method) { //在缓存的执行器中选择一个对应方法的执行器 AsyncTaskExecutor executor = (AsyncTaskExecutor)this.executors.get(method); if (executor == null) { //获取@Async注解中的value(指定的执行器) String qualifier = this.getExecutorQualifier(method); Executor targetExecutor; if (StringUtils.hasLength(qualifier)) { //获取指定执行器的bean targetExecutor = this.findQualifiedExecutor(this.beanFactory, qualifier); } else { //选择默认的执行器 targetExecutor = (Executor)this.defaultExecutor.get(); } if (targetExecutor == null) { return null; } executor = targetExecutor instanceof AsyncListenableTaskExecutor ? (AsyncListenableTaskExecutor)targetExecutor : new TaskExecutorAdapter(targetExecutor); //将执行器进行缓存 this.executors.put(method, executor); } return (AsyncTaskExecutor)executor; }

当有了执行器调用doSubmit方法将任务加入到执行器中。

异步任务,默认将采用SimpleAsyncTaskExecutor作为执行器!它有如下特点:

不复用线程,也就是说为每个任务新起一个线程。但是可以通过concurrencyLimit属性来控制并发线程数量,但是默认情况下不做限制(concurrencyLimit取值为-1)。
因此,如果我们使用异步任务,一定不能采用默认执行器的配置,以防OOM异常!最好的方式是指定执行器!

总结

本文主要以看源码的方式来了解异步注解@Async是如何在项目中选择线程以及使用线程的,尽量给异步任务指定一个独有线程池,这样会的避免不与其他业务共用线程池而造成影响。

AI大模型学习福利

作为一名热心肠的互联网老兵,我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

一、全套AGI大模型学习路线

AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获取

二、640套AI大模型报告合集

这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

三、AI大模型经典PDF籍

随着人工智能技术的飞速发展,AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型,如GPT-3、BERT、XLNet等,以其强大的语言理解和生成能力,正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。


因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

四、AI大模型商业化落地方案

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

作为普通人,入局大模型时代需要持续学习和实践,不断提高自己的技能和认知水平,同时也需要有责任感和伦理意识,为人工智能的健康发展贡献力量

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/7 21:00:03

andriod命令使用

http://tools.android-studio.org/index.php/sdk 下载sdk 显示设备adb devices 连接设备:adb connect IP&#xff0c;adb connect 127,0.0.1:7555 断开设置:adb disconnect IP &#xff0c;adb disconncct 127.0.0.1:7555 adb install -r -r 表示保留数据和缓存文件 adb insta…

作者头像 李华
网站建设 2026/2/8 5:22:55

并行进位与波纹进位8位加法器对比:门级实现详解

以下是对您提供的技术博文《并行进位与波纹进位8位加法器对比:门级实现详解》的 深度润色与结构重构版本 。本次优化严格遵循您的全部要求: ✅ 彻底去除AI腔调与模板化表达(如“本文将从……几个方面阐述”) ✅ 摒弃所有程式化标题(引言/概述/总结/展望),代之以自然…

作者头像 李华
网站建设 2026/2/7 4:14:04

Qwen3-4B在航空航天落地:技术文档术语统一+缩写表生成

Qwen3-4B在航空航天落地&#xff1a;技术文档术语统一缩写表生成 1. 为什么航空航天文档特别需要术语“翻译官” 你有没有翻过一份典型的航空航天技术手册&#xff1f;比如某型飞行器的《系统集成测试规范》或《航电设备维护指南》——密密麻麻几十页&#xff0c;满屏是“ADI…

作者头像 李华
网站建设 2026/2/6 15:53:35

ChatTTS效果展示:模拟真实人物对话的语音片段

ChatTTS效果展示&#xff1a;模拟真实人物对话的语音片段 1. 这不是“读出来”&#xff0c;是“说给你听” 你有没有听过那种语音合成&#xff1f;字正腔圆、节奏均匀、每个字都像用尺子量过一样精准——但越听越觉得不对劲&#xff0c;像在听一台精密仪器念说明书。 ChatTT…

作者头像 李华
网站建设 2026/2/6 19:31:35

AI手势识别与AR结合:增强现实手势交互部署案例

AI手势识别与AR结合&#xff1a;增强现实手势交互部署案例 1. 为什么手势正在成为AR交互的新入口 你有没有试过在AR眼镜里&#xff0c;想放大一张图片却只能靠语音“放大”&#xff0c;或者想翻页却得说“下一页”&#xff1f;听起来很酷&#xff0c;但实际用起来总有点别扭—…

作者头像 李华