当前位置: 首页 > 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领…...

谷歌浏览器插件

项目中有时候会用到插件 sync-cookie-extension1.0.0&#xff1a;开发环境同步测试 cookie 至 localhost&#xff0c;便于本地请求服务携带 cookie 参考地址&#xff1a;https://juejin.cn/post/7139354571712757767 里面有源码下载下来&#xff0c;加在到扩展即可使用FeHelp…...

Android Wi-Fi 连接失败日志分析

1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分&#xff1a; 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析&#xff1a; CTR…...

YSYX学习记录(八)

C语言&#xff0c;练习0&#xff1a; 先创建一个文件夹&#xff0c;我用的是物理机&#xff1a; 安装build-essential 练习1&#xff1a; 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件&#xff0c;随机修改或删除一部分&#xff0c;之后…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

Java编程之桥接模式

定义 桥接模式&#xff08;Bridge Pattern&#xff09;属于结构型设计模式&#xff0c;它的核心意图是将抽象部分与实现部分分离&#xff0c;使它们可以独立地变化。这种模式通过组合关系来替代继承关系&#xff0c;从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...

Unity中的transform.up

2025年6月8日&#xff0c;周日下午 在Unity中&#xff0c;transform.up是Transform组件的一个属性&#xff0c;表示游戏对象在世界空间中的“上”方向&#xff08;Y轴正方向&#xff09;&#xff0c;且会随对象旋转动态变化。以下是关键点解析&#xff1a; 基本定义 transfor…...

第八部分:阶段项目 6:构建 React 前端应用

现在&#xff0c;是时候将你学到的 React 基础知识付诸实践&#xff0c;构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段&#xff0c;你可以先使用模拟数据&#xff0c;或者如果你的后端 API&#xff08;阶段项目 5&#xff09;已经搭建好&#xff0c;可以直接连…...

【阅读笔记】MemOS: 大语言模型内存增强生成操作系统

核心速览 研究背景 ​​研究问题​​&#xff1a;这篇文章要解决的问题是当前大型语言模型&#xff08;LLMs&#xff09;在处理内存方面的局限性。LLMs虽然在语言感知和生成方面表现出色&#xff0c;但缺乏统一的、结构化的内存架构。现有的方法如检索增强生成&#xff08;RA…...

Easy Excel

Easy Excel 一、依赖引入二、基本使用1. 定义实体类&#xff08;导入/导出共用&#xff09;2. 写 Excel3. 读 Excel 三、常用注解说明&#xff08;完整列表&#xff09;四、进阶&#xff1a;自定义转换器&#xff08;Converter&#xff09; 其它自定义转换器没生效 Easy Excel在…...