当前位置: 首页 > news >正文

Spring Boot @Async 注解深度指南

Spring Boot @Async 注解深度指南


一、核心使用要点
  1. 启用异步支持

    • 必须在启动类或配置类添加 @EnableAsync,否则异步不生效。
    @SpringBootApplication
    @EnableAsync
    public class Application { ... }
    
  2. 线程池配置

    • 默认问题:Spring 默认使用 SimpleAsyncTaskExecutor(每次新建线程),生产环境需自定义线程池
    • 推荐配置:通过 ThreadPoolTaskExecutor 定义核心参数(核心线程数、队列容量等):
      @Configuration
      @EnableAsync
      public class AsyncConfig implements AsyncConfigurer {@Overridepublic Executor getAsyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(5);executor.setMaxPoolSize(10);executor.setQueueCapacity(100);executor.setThreadNamePrefix("Async-");executor.initialize();  // 必须初始化return executor;}
      }
      
  3. 方法调用限制

    • 同类调用失效禁止在同一个类中直接调用 @Async 方法(如 this.asyncMethod()),需通过代理对象调用。
    • 解决方案:将异步方法拆分到独立类中,通过依赖注入调用:
      @Service
      public class ServiceA {@Autowiredprivate ServiceB serviceB;  // 异步方法在 ServiceB 中定义public void callAsync() {serviceB.asyncMethod();  // 通过代理对象调用}
      }
      

二、常见失效场景及解决方案
  1. 方法修饰符错误

    • 限制@Async 仅对 public 方法生效,private/static/final 方法无效。
  2. 事务管理冲突

    • 问题:异步方法默认不继承事务上下文,@Transactional 可能失效。
    • 解决方案
      • 在异步方法内部显式管理事务。
      • 使用分布式事务框架(如 Seata)。
  3. 异常处理缺失

    • 默认行为:异步方法抛出的异常不会传播到调用线程,需通过 FutureAsyncUncaughtExceptionHandler 捕获。
    • 全局异常处理配置
      @Override
      public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return (ex, method, params) -> log.error("异步方法 {} 异常: {}", method.getName(), ex.getMessage());
      }
      

三、进阶注意事项
  1. 线程上下文传递

    • 问题:异步线程默认不继承主线程的上下文(如 MDC 日志跟踪、SecurityContext)。
    • 解决方案:通过 TaskDecorator 装饰任务,手动传递上下文:
      executor.setTaskDecorator(task -> {Map<String, String> context = MDC.getCopyOfContextMap();  // 获取主线程上下文return () -> {MDC.setContextMap(context);  // 设置到异步线程task.run();MDC.clear();};
      });
      
  2. 与定时任务结合

    • 风险:在 @Scheduled@XxlJob 标注的方法上直接使用 @Async,可能导致调度平台无法感知任务结果。
    • 建议:仅在任务内部耗时操作中使用异步,而非整个方法。
  3. 资源释放

    • 线程池关闭:Spring 管理的 ThreadPoolTaskExecutor 会在应用关闭时自动调用 shutdown(),但需设置等待任务完成:
      executor.setWaitForTasksToCompleteOnShutdown(true);  // 等待任务完成
      executor.setAwaitTerminationSeconds(60);             // 最长等待时间
      

四、实际案例解析
案例 1:基础异步调用(无返回值)

场景:执行耗时任务(如日志记录、消息推送)时不阻塞主线程。
代码实现

@Service
public class NotificationService {@Asyncpublic void sendEmail(String content) {System.out.println("异步发送邮件中,线程:" + Thread.currentThread().getName());// 模拟耗时操作try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); }System.out.println("邮件发送完成!");}
}@RestController
public class UserController {@Autowiredprivate NotificationService notificationService;@PostMapping("/register")public String registerUser() {notificationService.sendEmail("欢迎注册!"); // 异步执行return "注册成功,邮件发送中..."; // 主线程立即返回}
}

关键点

  • 方法需标记为 public,且类需被 Spring 管理(如 @Service);
  • 主线程调用后立即返回,任务由 SimpleAsyncTaskExecutor 默认线程池执行。

案例 2:带返回值的异步任务

场景:异步执行任务并获取结果(如批量数据处理)。
代码实现

@Service
public class DataProcessService {@Asyncpublic CompletableFuture<List<String>> processData(List<String> data) {System.out.println("异步处理数据,线程:" + Thread.currentThread().getName());// 模拟耗时处理List<String> result = data.stream().map(String::toUpperCase).collect(Collectors.toList());return CompletableFuture.completedFuture(result);}
}@RestController
public class DataController {@Autowiredprivate DataProcessService dataProcessService;@GetMapping("/process")public CompletableFuture<String> process() {return dataProcessService.processData(Arrays.asList("a", "b", "c")).thenApply(result -> "处理结果:" + result);}
}

关键点

  • 返回值需用 CompletableFutureFuture 包装;
  • 调用方通过 thenApplyget() 获取结果(注意阻塞风险)。

案例 3:自定义线程池配置

场景:优化线程资源,避免默认线程池的性能问题。
配置类: 注意:这里 使用 @Bean(‘线程池名称’) 注解 或 实现 AsyncConfigurer 可任选其一 也可都实现, 实现AsyncConfigurer 代表将 将spring默认线程池替换为 当前线程池(默认线程池存在问题:“Spring 默认使用 SimpleAsyncTaskExecutor(每次新建线程),生产环境需自定义线程池。”),使用 @Bean() 则需要给当前线程池的指定bean名称,在使用 @Async(‘线程池名称’)注解时,就需要指定线程池的名称了,当然这个案例中这里不是必须的,当@Async不指定线程池名称时,则使用的时是默认线程池,在这个案例中,默认的线程池也就是 ‘customExecutor’。

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {@Bean("customExecutor")public Executor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(5);executor.setMaxPoolSize(10);executor.setQueueCapacity(100);executor.setThreadNamePrefix("Custom-Async-");executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}
}

使用示例

@Service
public class ReportService {@Async("customExecutor") // 指定线程池public void generateReport() {System.out.println("生成报表中,线程:" + Thread.currentThread().getName());}
}

关键点

  • 通过 @Async("beanName") 指定线程池;
  • 拒绝策略推荐 CallerRunsPolicy 避免任务丢失。

案例 4:异步事务管理

场景:异步方法中操作数据库并保证事务一致性。
代码实现

@Service
public class OrderService {@Autowiredprivate OrderRepository orderRepository;@Async@Transactional(propagation = Propagation.REQUIRES_NEW)public void asyncCreateOrder(Order order) {orderRepository.save(order); // 事务独立提交if (order.getAmount() < 0) {throw new RuntimeException("金额异常"); // 触发回滚}}
}

关键点

  • 异步方法需添加 @Transactional 并指定传播行为(如 REQUIRES_NEW);
  • 主线程事务与异步线程事务相互隔离。

案例 5:全局异常处理

场景:捕获异步方法中的未处理异常。

(1)默认行为与风险

1. 无返回值方法异常静默丢弃

例如:

@Async
public void asyncTask() {throw new RuntimeException("异步异常"); // 无日志、无处理
}

异常会被 Spring 默认的 SimpleAsyncUncaughtExceptionHandler 处理,仅打印 ERROR 级别日志,但无具体堆栈信息。

2. 调试困难

异步线程的异常不会传播到调用线程,若未记录日志,可能无法发现潜在问题。


(2)最佳实践

1. 无返回值处理异常:

通过自定义异常处理器,统一记录日志或发送告警:

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {@Overridepublic AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return (ex, method, params) -> {// 记录日志或发送告警log.error("异步方法 {} 异常,参数: {}", method.getName(), Arrays.toString(params), ex);// 发送邮件/企业微信通知(可选)};}
}
使用示例:
@Service
public class NotificationService {@Asyncpublic void sendEmail() {throw new RuntimeException("邮件发送失败");}
}
  • 触发场景:调用 sendEmail() 时,异常会被 AsyncUncaughtExceptionHandler 捕获并记录日志。
2. 结合返回值处理异常:
1. 通过 Future.get() 捕获异常

使用 CompletableFuture 包装返回值,调用 get() 时显式捕获异常。

@Service
public class DataService {@Asyncpublic CompletableFuture<String> processData() {return CompletableFuture.supplyAsync(() -> {if (error) throw new RuntimeException("数据处理异常");return "处理结果";});}
}@RestController
public class DataController {@Autowiredprivate DataService dataService;@GetMapping("/process")public String process() {try {return dataService.processData().get();} catch (InterruptedException | ExecutionException e) {return "处理失败: " + e.getCause().getMessage();}}
}
2. 通过 exceptionally() 链式处理

利用 CompletableFuture 的链式异常处理:

@Async
public CompletableFuture<String> asyncTask() {return CompletableFuture.supplyAsync(() -> {throw new RuntimeException("任务失败");}).exceptionally(ex -> {log.error("任务异常", ex);return "默认值";});
}
注意事项
  • 阻塞风险Future.get() 会阻塞主线程,需结合超时机制(如 get(5, TimeUnit.SECONDS))。
  • 线程池隔离:建议为耗时任务配置独立线程池,避免核心业务线程池被阻塞。

总结与最佳实践

场景策略适用场景
无返回值 + 无需关注结果必须实现 getAsyncUncaughtExceptionHandler日志记录、消息推送等非关键任务
无返回值 + 需关注结果重构为有返回值方法,或实现异常处理器数据同步、状态更新等关键任务
有返回值优先通过 FutureCompletableFuture 处理异常批量处理、复杂计算等需返回结果的任务

最佳实践

  1. 混合使用:对关键任务使用 CompletableFuture 返回值,非关键任务用 void + 全局处理器。
  2. 监控告警:在 AsyncUncaughtExceptionHandler 中集成 Sentry 或 Prometheus 监控。
  3. 事务拆分:异步方法涉及数据库操作时,确保事务边界清晰(如拆分到独立服务)。
  4. 线程池隔离:为耗时任务配置独立线程池,避免核心业务线程池被阻塞。
**案例 6:定时任务异步化
  • 场景:XXL-JOB 任务异步执行,避免阻塞调度线程。
  • 代码示例
    @Component
    @XxlJob("generateTask")
    public class TaskJob {@Asyncpublic void generateTask(String param) {// 异步执行耗时任务}
    }
    
  • 风险:调度平台无法获取执行结果,需通过日志或回调机制跟踪状态。

五、性能优化与监控
  1. 参数调优

    • 核心公式
      • 核心线程数 ≈ CPU 核心数 × 2
      • 队列容量根据任务平均耗时调整(避免 OOM)。
  2. 拒绝策略选择

    • 生产推荐:使用 CallerRunsPolicy(由调用线程执行任务),避免任务丢失:
      executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
      
  3. 监控指标

    • 关键指标:活跃线程数 (getActiveCount())、队列大小 (getQueue().size())、已完成任务数 (getCompletedTaskCount())。

总结与最佳实践

场景技术方案
简单异步任务无返回值方法 + 默认线程池
结果依赖任务CompletableFuture 包装返回值
高并发优化自定义 ThreadPoolTaskExecutor
数据库事务@Transactional + 独立传播行为
异常处理AsyncUncaughtExceptionHandler

注意事项

  1. 避免同类内调用 @Async 方法(需通过代理对象调用);
  2. 监控线程池状态(活跃线程数、队列堆积)防止资源耗尽;
  3. 异步方法中谨慎使用 ThreadLocal,需通过 TaskDecorator 传递上下文。

相关文章:

Spring Boot @Async 注解深度指南

Spring Boot Async 注解深度指南 一、核心使用要点 启用异步支持 必须在启动类或配置类添加 EnableAsync&#xff0c;否则异步不生效。 SpringBootApplication EnableAsync public class Application { ... }线程池配置 默认问题&#xff1a;Spring 默认使用 SimpleAsyncTaskEx…...

windows设置暂停更新时长

windows设置暂停更新时长 win11与win10修改注册表操作一致 &#xff0c;系统界面不同 1.打开注册表 2.在以下路径 \HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings 右键新建 DWORD 32位值&#xff0c;名称为FlightSettingsMaxPauseDays 根据需求填写数…...

Orange 开源项目 - 集成百度智能云-千帆大模型

1 集成百度智能云-千帆大模型 百度智能云-千帆ModelBuilder百度智能云千帆大模型服务与开发平台ModelBuilder&#xff08;以下简称千帆ModelBuilder&#xff09;是面向企业开发者的一站式大模型开发及服务运行平台。千帆ModelBuilder不仅提供了包括文心一言底层模型和第三方开源…...

特斯拉 FSD 算法深度剖析:软件层面全解读

一、引言 特斯拉的 FSD&#xff08;Full Self-Driving&#xff09;系统作为自动驾驶领域的前沿成果&#xff0c;其软件层面的算法设计至关重要。本文将从软件的角度&#xff0c;深入探讨特斯拉 FSD 所采用的算法&#xff0c;包括感知、规划、控制等多个方面&#xff0c;以期为…...

2025/2/17--2/23学习笔记(week1)_C语言

1 整数的存储 只有整数才有原码&#xff0c;反码&#xff0c;补码&#xff0c;原码取反加一&#xff08;除了符号位&#xff09;得到补码。补码的补码会变成原码。 在任何位运算里&#xff0c;都是操作的补码&#xff0c;因为整数在内存里都是以补码存储的 2 移位运算符 移位…...

数据结构:二叉树的数组结构以及堆的实现详解

目录 一.树与二叉树 1.树的概念与相关术语&#xff1a; 2.二叉树&#xff1a; &#xff08;1&#xff09;定义&#xff1a; &#xff08;2&#xff09;特殊的二叉树&#xff1a; &#xff08;3&#xff09;完全二叉树 &#xff08;4&#xff09;二叉树的存储结构&#x…...

AWS S3 如何设置公开访问权限?

1.让整个bucket都有公开访问权限 1.1关闭【阻止公共读】 1.2关闭ACL访问控制 1.3打开桶策略 这样桶内所有的图片就能访问了 2.只开放特定文件让其具有访问权限&#xff1f; 2.1关闭【阻止公共读】 如之前的图示 2.2打开ACL控制 2.3单个文件打开公共读...

使用TortoiseGit配合BeyondCompare实现在Git仓库中比对二进制文件

使用TortoiseGit的比对工具可以直接右键&#xff0c;点击选择比对和上一版本的变化差异&#xff1a; 但是TortoiseGit只能支持比对纯文本文件的变化差异&#xff0c;如果尝试比对二进制文件&#xff0c;会提示这不是一个有效的文本文件&#xff1a; BeyondCompare可以比对二进制…...

8、HTTP/1.0和HTTP/1.1的区别【高频】

第一个是 长连接&#xff1a; HTTP/1.0 默认 短连接&#xff0c;&#xff08;它也可以指定 Connection 首部字段的值为 Keep-Alive实现 长连接&#xff09;而HTTP/1.1 默认支持 长连接&#xff0c;HTTP/1.1是基于 TCP/IP协议的&#xff0c;创建一个TCP连接是需要经过三次握手的…...

Rk3568驱动开发_开发环境的搭建_1

1、环境说明&#xff1a; 需要用官方的程序包&#xff0c;这个程序需要在虚拟机里编译再将镜像烧录到板子里&#xff0c;本质上是给板子上一套Linux操作系统&#xff0c;镜像是.img文件 Linux操作系统被分成了多个模块&#xff0c;编译好后储存在镜像里&#xff0c;本质上就和…...

Solr中得Core和Collection的作用和关系

Solr中得Core和Collection的作用和关系 一&#xff0c; 总结 在Apache Solr中&#xff0c;Core和Collection 是两个核心概念&#xff0c;他们分别用于单机模式和分布式模式&#xff08;SolrCloud&#xff09;中&#xff0c;用于管理和组织数据。 二&#xff0c;Core 定义&am…...

Visual Studio Code 远程开发方法

方法1 共享屏幕远程控制&#xff0c;如 to desk, 向日葵 &#xff0c;像素太差&#xff0c;放弃 方法2 内网穿透 ssh 第二个方法又很麻烦&#xff0c;尤其是对于 windows 电脑&#xff0c;要使用 ssh 还需要额外安装杂七杂八的东西&#xff1b;并且内网穿透服务提供商提供的…...

如何看到 git 上打 tag 的时间

在 Git 中可以通过以下方法查看标签&#xff08;tag&#xff09;的创建时间&#xff1a; 使用 git show 命令&#xff1a; 运行以下命令可以查看某个特定标签的详细信息&#xff0c;包括创建时间&#xff1a; git show 输出中会包含 Tagger 的信息和 Date 字段&#xff0c;显示…...

【HarmonyOS Next】鸿蒙TaskPool和Worker详解 (一)

【HarmonyOS Next】鸿蒙TaskPool和Worker详解 &#xff08;一&#xff09; 一、TaskPool和Worker如何实现多线程&#xff1f;各自特点是什么&#xff1f; 在鸿蒙中通过TaskPool和Worker实现多线程并发&#xff0c;两者都基于Actor并发模型实现。 Actor并发模型&#xff0c;每…...

如何设置HTTPOnly和Secure Cookie标志?

设置HttpOnly和Secure标志于Cookie中是增强Web应用安全性的重要措施。这两个标志帮助防止跨站脚本攻击&#xff08;XSS&#xff09;和中间人攻击&#xff08;MitM&#xff09;。下面是关于如何设置这些标志的具体步骤&#xff1a; 设置方法 在服务器端设置 根据你的服务器端…...

几个api

几个api 原型链 可以阅读此文 Function instanceof Object // true Object instanceof Function // true Object.prototype.isPrototypeOf(Function) // true Function.prototype.isPrototypeOf(Object) // true Object.__proto__ Function.prototype // true Function.pro…...

Deepseek本地部署指南:在linux服务器部署,在mac远程web-ui访问

1. 在Linux服务器上部署DeepSeek模型 要在 Linux 上通过 Ollama 安装和使用模型&#xff0c;您可以按照以下步骤进行操作&#xff1a; 步骤 1&#xff1a;安装 Ollama 安装 Ollama&#xff1a; 使用以下命令安装 Ollama&#xff1a; curl -sSfL https://ollama.com/install.s…...

基于 DeepSeek+AutoGen 的智能体协作系统

用 AutoGen 实现智能体协作流程&#xff0c;假设团队里的 3 个角色&#xff0c;让 3 个角色相互交流后并给出不同方案&#xff0c;最后进行总结。下面是实现的思路&#xff0c;欢迎一起学习交流。  一、系统设计 1. sre_engineer_01 - 问题诊断与初步解决方案 职责&#xff1a…...

博客系统笔记总结 2( Linux 相关)

Linux 基本使用和程序部署 基本命令 文件操作 显示当前目录下的文件 ls&#xff1a;显示当前目录下的文件 ll&#xff1a;以列表的形式展示&#xff0c;包括隐藏文件 进入目录 && 显示当前路径 cd&#xff1a;进入目录&#xff08;后面跟相对路径或者绝对路径&…...

计算机毕业设计SpringBoot+Vue.js电影评论网站系统(源码+文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…...

毕业设计 yolov11骨折检测医疗辅助系统(源码+论文)

文章目录 0 前言1 项目运行效果2 课题背景2.1 研究背景2.2 国内外研究现状2.3 研究意义 3 设计框架&#xff08;骨折检测系统设计框架说明&#xff09;3.1. 系统架构图3.2. 技术选型3.2.1 核心组件3.2.2 辅助工具 3.3. 核心模块设计3.3.1 YOLO模型训练模块训练流程图关键伪代码…...

如何快速掌握MoveIt2:面向ROS 2开发者的工业机器人运动规划完整指南

如何快速掌握MoveIt2&#xff1a;面向ROS 2开发者的工业机器人运动规划完整指南 【免费下载链接】moveit2 :robot: MoveIt for ROS 2 项目地址: https://gitcode.com/gh_mirrors/mo/moveit2 想要为你的机器人实现智能运动规划吗&#xff1f;MoveIt2作为ROS 2生态中最强大…...

在数据预处理与分析流水线中集成大模型API进行智能标注与摘要

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 在数据预处理与分析流水线中集成大模型API进行智能标注与摘要 对于数据工程师而言&#xff0c;处理海量非结构化文本数据是一项常见…...

3PEAK思瑞浦 TPA6531-S5TR SOT23-5 运算放大器

特性 供电电压:1.75V至5.5V 偏移电压:1.5mV(最大值) 最大可调工作频率:300kHz&#xff0c;斜率:0.15V/us 轨到轨输入和输出 0.1赫兹至10赫兹电压噪声:1伏峰值 开关电源时无显著输出抖动 低功耗:每通道最大25安培 工作温度范围:-40C至125C...

仅限首批200位架构师获取:DeepSeek-DDD联合建模工作坊实录(含领域事件风暴原始会议录像+决策日志)

更多请点击&#xff1a; https://kaifayun.com 第一章&#xff1a;DeepSeek领域驱动设计的范式演进与本质洞察 DeepSeek作为面向大规模智能体协同与复杂业务语义建模的新一代AI原生架构&#xff0c;其领域驱动设计&#xff08;DDD&#xff09;实践已突破传统分层单体范式&…...

语音AI落地最后一公里卡点,PlayAI质量波动真相:采样率适配缺陷、韵律断层、情感衰减三大隐性陷阱

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;PlayAI语音质量评测报告总览 PlayAI语音质量评测体系基于客观指标与主观听感双维度构建&#xff0c;覆盖清晰度、自然度、时延、抗噪性及情感一致性五大核心能力。本报告汇总了在标准测试集&#xff08…...

Claude服务治理架构升级(生产环境零停机迁移实录)

更多请点击&#xff1a; https://codechina.net 第一章&#xff1a;Claude服务治理架构升级&#xff08;生产环境零停机迁移实录&#xff09; 为应对日益增长的推理请求量与多租户策略精细化需求&#xff0c;我们对Claude服务治理层实施了从单体API网关向云原生服务网格的平滑…...

如何快速配置虚拟显示器:面向初学者的完整指南

如何快速配置虚拟显示器&#xff1a;面向初学者的完整指南 【免费下载链接】parsec-vdd ✨ Perfect virtual display for game streaming 项目地址: https://gitcode.com/gh_mirrors/pa/parsec-vdd 你是否在为游戏串流画质不佳而烦恼&#xff1f;或者需要为无显示器主机…...

Taotoken的APIKey管理与访问控制功能保障了企业级安全

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 Taotoken的APIKey管理与访问控制功能保障了企业级安全 当团队开始规模化使用大语言模型时&#xff0c;一个核心挑战随之而来&#…...

KMS智能激活工具:如何一键永久激活Windows和Office的完整指南

KMS智能激活工具&#xff1a;如何一键永久激活Windows和Office的完整指南 【免费下载链接】KMS_VL_ALL_AIO Smart Activation Script 项目地址: https://gitcode.com/gh_mirrors/km/KMS_VL_ALL_AIO 还在为Windows和Office激活问题而烦恼吗&#xff1f;每次系统重装后都要…...