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

测试微信模版消息推送

进入“开发接口管理”--“公众平台测试账号”&#xff0c;无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息&#xff1a; 关注测试号&#xff1a;扫二维码关注测试号。 发送模版消息&#xff1a; import requests da…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

逻辑回归:给不确定性划界的分类大师

想象你是一名医生。面对患者的检查报告&#xff08;肿瘤大小、血液指标&#xff09;&#xff0c;你需要做出一个**决定性判断**&#xff1a;恶性还是良性&#xff1f;这种“非黑即白”的抉择&#xff0c;正是**逻辑回归&#xff08;Logistic Regression&#xff09;** 的战场&a…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比

在机器学习的回归分析中&#xff0c;损失函数的选择对模型性能具有决定性影响。均方误差&#xff08;MSE&#xff09;作为经典的损失函数&#xff0c;在处理干净数据时表现优异&#xff0c;但在面对包含异常值的噪声数据时&#xff0c;其对大误差的二次惩罚机制往往导致模型参数…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

Kafka入门-生产者

生产者 生产者发送流程&#xff1a; 延迟时间为0ms时&#xff0c;也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于&#xff1a;异步发送不需要等待结果&#xff0c;同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...

LRU 缓存机制详解与实现(Java版) + 力扣解决

&#x1f4cc; LRU 缓存机制详解与实现&#xff08;Java版&#xff09; 一、&#x1f4d6; 问题背景 在日常开发中&#xff0c;我们经常会使用 缓存&#xff08;Cache&#xff09; 来提升性能。但由于内存有限&#xff0c;缓存不可能无限增长&#xff0c;于是需要策略决定&am…...

spring Security对RBAC及其ABAC的支持使用

RBAC (基于角色的访问控制) RBAC (Role-Based Access Control) 是 Spring Security 中最常用的权限模型&#xff0c;它将权限分配给角色&#xff0c;再将角色分配给用户。 RBAC 核心实现 1. 数据库设计 users roles permissions ------- ------…...