Spring Boot @Async 注解深度指南
Spring Boot @Async 注解深度指南
一、核心使用要点
-
启用异步支持
- 必须在启动类或配置类添加
@EnableAsync,否则异步不生效。
@SpringBootApplication @EnableAsync public class Application { ... } - 必须在启动类或配置类添加
-
线程池配置
- 默认问题: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;} }
- 默认问题:Spring 默认使用
-
方法调用限制
- 同类调用失效:禁止在同一个类中直接调用
@Async方法(如this.asyncMethod()),需通过代理对象调用。 - 解决方案:将异步方法拆分到独立类中,通过依赖注入调用:
@Service public class ServiceA {@Autowiredprivate ServiceB serviceB; // 异步方法在 ServiceB 中定义public void callAsync() {serviceB.asyncMethod(); // 通过代理对象调用} }
- 同类调用失效:禁止在同一个类中直接调用
二、常见失效场景及解决方案
-
方法修饰符错误
- 限制:
@Async仅对public方法生效,private/static/final方法无效。
- 限制:
-
事务管理冲突
- 问题:异步方法默认不继承事务上下文,
@Transactional可能失效。 - 解决方案:
- 在异步方法内部显式管理事务。
- 使用分布式事务框架(如 Seata)。
- 问题:异步方法默认不继承事务上下文,
-
异常处理缺失
- 默认行为:异步方法抛出的异常不会传播到调用线程,需通过
Future或AsyncUncaughtExceptionHandler捕获。 - 全局异常处理配置:
@Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return (ex, method, params) -> log.error("异步方法 {} 异常: {}", method.getName(), ex.getMessage()); }
- 默认行为:异步方法抛出的异常不会传播到调用线程,需通过
三、进阶注意事项
-
线程上下文传递
- 问题:异步线程默认不继承主线程的上下文(如 MDC 日志跟踪、SecurityContext)。
- 解决方案:通过
TaskDecorator装饰任务,手动传递上下文:executor.setTaskDecorator(task -> {Map<String, String> context = MDC.getCopyOfContextMap(); // 获取主线程上下文return () -> {MDC.setContextMap(context); // 设置到异步线程task.run();MDC.clear();}; });
-
与定时任务结合
- 风险:在
@Scheduled或@XxlJob标注的方法上直接使用@Async,可能导致调度平台无法感知任务结果。 - 建议:仅在任务内部耗时操作中使用异步,而非整个方法。
- 风险:在
-
资源释放
- 线程池关闭:Spring 管理的
ThreadPoolTaskExecutor会在应用关闭时自动调用shutdown(),但需设置等待任务完成:executor.setWaitForTasksToCompleteOnShutdown(true); // 等待任务完成 executor.setAwaitTerminationSeconds(60); // 最长等待时间
- 线程池关闭:Spring 管理的
四、实际案例解析
案例 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);}
}
关键点:
- 返回值需用
CompletableFuture或Future包装; - 调用方通过
thenApply或get()获取结果(注意阻塞风险)。
案例 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 | 日志记录、消息推送等非关键任务 |
| 无返回值 + 需关注结果 | 重构为有返回值方法,或实现异常处理器 | 数据同步、状态更新等关键任务 |
| 有返回值 | 优先通过 Future 或 CompletableFuture 处理异常 | 批量处理、复杂计算等需返回结果的任务 |
最佳实践:
- 混合使用:对关键任务使用
CompletableFuture返回值,非关键任务用void+ 全局处理器。 - 监控告警:在
AsyncUncaughtExceptionHandler中集成 Sentry 或 Prometheus 监控。 - 事务拆分:异步方法涉及数据库操作时,确保事务边界清晰(如拆分到独立服务)。
- 线程池隔离:为耗时任务配置独立线程池,避免核心业务线程池被阻塞。
**案例 6:定时任务异步化
- 场景:XXL-JOB 任务异步执行,避免阻塞调度线程。
- 代码示例:
@Component @XxlJob("generateTask") public class TaskJob {@Asyncpublic void generateTask(String param) {// 异步执行耗时任务} } - 风险:调度平台无法获取执行结果,需通过日志或回调机制跟踪状态。
五、性能优化与监控
-
参数调优
- 核心公式:
- 核心线程数 ≈ CPU 核心数 × 2
- 队列容量根据任务平均耗时调整(避免 OOM)。
- 核心公式:
-
拒绝策略选择
- 生产推荐:使用
CallerRunsPolicy(由调用线程执行任务),避免任务丢失:executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
- 生产推荐:使用
-
监控指标
- 关键指标:活跃线程数 (
getActiveCount())、队列大小 (getQueue().size())、已完成任务数 (getCompletedTaskCount())。
- 关键指标:活跃线程数 (
总结与最佳实践
| 场景 | 技术方案 |
|---|---|
| 简单异步任务 | 无返回值方法 + 默认线程池 |
| 结果依赖任务 | CompletableFuture 包装返回值 |
| 高并发优化 | 自定义 ThreadPoolTaskExecutor |
| 数据库事务 | @Transactional + 独立传播行为 |
| 异常处理 | AsyncUncaughtExceptionHandler |
注意事项:
- 避免同类内调用
@Async方法(需通过代理对象调用); - 监控线程池状态(活跃线程数、队列堆积)防止资源耗尽;
- 异步方法中谨慎使用
ThreadLocal,需通过TaskDecorator传递上下文。
相关文章:
Spring Boot @Async 注解深度指南
Spring Boot Async 注解深度指南 一、核心使用要点 启用异步支持 必须在启动类或配置类添加 EnableAsync,否则异步不生效。 SpringBootApplication EnableAsync public class Application { ... }线程池配置 默认问题:Spring 默认使用 SimpleAsyncTaskEx…...
Facebook Instant Game:即时游戏的新时代
一、Facebook Instant Game 简介 Facebook Instant Game(即时游戏)是一种基于 HTML5 技术的轻量级游戏平台,允许用户无需下载即可在Facebook Messenger 和 News Feed 中直接游玩。这种游戏模式降低了用户的进入门槛,使得游戏可以迅速传播,极…...
取topN不同算法的实现的性能差别
背景 最近在实现一个需求,需要对大量数据中排序出前N,最暴力的方法肯定是直接全量排序。这里很明显是可以不用全量排序的,取前N,我们自然而然可以想到一个算法——堆排序。 一开始自己先写好了一版,后来想起ÿ…...
【实战 ES】实战 Elasticsearch:快速上手与深度实践-1.1.2典型应用场景:日志分析、实时搜索、推荐系统
👉 点击关注不迷路 👉 点击关注不迷路 👉 点击关注不迷路 文章大纲 为什么选择Elasticsearch?——典型应用场景深度解析1. 引言2. 日志分析:海量数据的实时洞察2.1 行业痛点2.2 ES解决方案关键技术实现: 2.…...
Spring Cloud Alibaba学习 3- Sentinel入门使用
Spring Cloud Alibaba学习 3- Sentinel入门使用 中文文档参考:Sentinel中文文档 一. SpringCloud整合Sentinel 1.1 下载Sentinel-Dashboard Sentinel下载地址:Sentinel-Dashboard 到下载目录,cmd输入 java -jar sentinel-dashboard-1.8…...
使用DeepSeek/chatgpt等AI工具辅助网络协议流量数据包分析
随着deepseek,chatgpt等大模型的能力越来越强大,本文将介绍一下deepseek等LLM在分数流量数据包这方面的能力。为需要借助LLM等大模型辅助分析流量数据包的同学提供参考,也了解一下目前是否有必要继续学习wireshark工具以及复杂的协议知识。 pcap格式 目…...
C语言 --- 经典习题1
C语言 --- 经典习题1 第 一 题 - - - 交 换 两 个 整 数 的 值(四 种 方 法)第 二 题 - - - 最 大 公 约 数 和 最 小 公 倍 数 之 和总结 💻作者简介:曾 与 你 一 样 迷 茫,现 以 经 验 助 你 入 门 C 语 言 …...
自定义mybatis拦截器,在springboot项目中不起作用的解决方法
自定义mybatis拦截器,在springboot项目中不起作用的解决方法 自定义mybatis拦截器,在若依springboot项目中不起作用的原因 找到 MyBatisConfig 配置类,引入自定义配置 在sqlSessionFactory中添加自定义拦截器,就可以正常使用了…...
记一次pytorch训练loss异常的问题
记一次pytorch训练loss异常的问题 问题描述 使用mmdetection框架训练时,某项loss出现异常大的值,比如1781232349724294.000。这个问题只在多卡训练时才会出现。 解决方法 在确认target和predction没有问题后,发现是在dataset中的数据处理…...
vue2使用d3.js实现网络拓扑图
vue2使用d3.js实现网络拓扑图 支持节点更换图标, 线条改变颜色 安装 npm i d37.9.0 --save自定义组件 <template><div style"width: 100%; height: 100%"><div ref"networkChart" class"network-chart"></div><e…...
记录一下在k3s快速创建gitlab
废话不多说,直接上配置文件 需要修改的地方(备注都有写): 1.命名空间 namespace 2. claimName 文件挂载 Deployment kind: Deployment apiVersion: apps/v1 metadata:name: gitlabnamespace: cicd # 替换为您的命名空间la…...
AWQ和GPTQ量化的区别
一、前言 本地化部署deepseek时发现,如果是量化版的deepseek,会节约很多的内容,然后一般有两种量化技术,那么这两种量化技术有什么区别呢? 二、量化技术对比 在模型量化领域,AWQ 和 GPTQ 是两种不同的量…...
线性模型 - 支持向量机
支持向量机(SVM)是一种用于分类(和回归)的监督学习算法,其主要目标是找到一个最佳决策超平面,将数据点分为不同的类别,并且使得分类边界与最近的数据点之间的间隔(margin)…...
AI大模型-提示工程学习笔记20-多模态思维链提示
目录 1. 多模态思维链提示的核心思想 (1) 单模态 CoT 的局限性 (2) Multimodal CoT 的解决方案 2. Multimodal CoT 的工作流程 (1) 多模态输入 (2) 特征提取 (3) 多模态融合 (4) 思维链生成 (5) 答案生成 3. Multimodal CoT 的关键组件 (1) 大语言模型 (LLM) (2) 多…...
nginx 搭建 IPv6 -> IPv4 反向代理服务器
背景 在实际生产过程中,由于各种原因,我们的在线服务搭建在火山云服务器上,使用火山云包括 ECS、CLB、PLB 等组件进行网络通信,并且通过专线接受来自某公司内部流量。但是在大概 22~23 年,某公司要把所有网络流量变为…...
湖北中医药大学谱度众合(武汉)生命科技有限公司研究生工作站揭牌
2025年2月11日,湖北中医药大学&谱度众合(武汉)生命科技有限公司研究生工作站揭牌仪式在武汉生物技术研究院一楼101会议室举行,湖北中医药大学研究生院院长刘娅教授、基础医学院院长孔明望教授、基础医学院赵敏教授、基础医学院…...
面试基础---深入解析 AQS
深入解析 AQS:从源码到实践,剖析 ReentrantLock 和 Semaphore 的实现 引言 在 Java 并发编程中,AbstractQueuedSynchronizer(AQS)是一个核心框架,它为构建锁和其他同步器提供了基础支持。ReentrantLock 和…...
go 语言中的线程池
使用 goroutine 和 channel Go 语言中并没有直接类似 Java 线程池的内建概念,但它提供了类似的功能,主要通过goroutine和channel来实现并发处理。你可以通过结合这两者来实现一个“线程池”的功能。 在 Go 中,goroutine是轻量级的线程&…...
从 0 到 1,用 Python 构建超实用 Web 实时聊天应用
从 0 到 1,用 Python 构建超实用 Web 实时聊天应用 本文深入剖析如何运用 Python 的 Flask 框架与 SocketIO 扩展,搭建一个功能完备的 Web 实时聊天应用。从环境搭建、前后端代码实现,到最终运行展示,逐步拆解关键步骤࿰…...
AF3 DataPipeline类process_multiseq_fasta 方法解读
AlphaFold3 data_pipeline 模块DataPipeline类的 process_multiseq_fasta 方法用于处理多序列 FASTA 文件,生成 AlphaFold3 结构预测所需的特征,适用于多链复合物的预测。它结合了 Minkyung Baek 在 Twitter 上提出的“AlphaFold-Gap”策略,即通过在多链 MSA 中插入固定长度…...
Vue2+Element实现Excel文件上传下载预览【超详细图解】
目录 一、需求背景 二、落地实现 1.文件上传 图片示例 HTML代码 业务代码 2.文件下载 图片示例 方式一:代码 方式二:代码 3.文件预览 图片示例 方式一:代码 方式二:代码 一、需求背景 在一个愉快的年后ÿ…...
[记录贴] 火绒奇怪的进程保护
最近一次更新火绒6.0到最新版,发现processhacker的结束进程功能无法杀掉火绒的进程,弹窗提示如下: 可能是打开进程时做了权限过滤,火绒注册了两个回调函数如下: 但奇怪的是,在另外一台机器上面更新到最新版…...
【蓝桥杯】每天一题,理解逻辑(1/90)【Leetcode 移动零】
文章目录 题目解析讲解算法原理【双指针算法思路】(数组下标充当指针)如何划分和执行过程大致 代码详情 题目解析 题目链接:https://leetcode.cn/problems/move-zeroes/description/ 题目意思解析 把所有的零移动到数组的末尾保持非零元素的相对顺序 理解了这两层…...
vue js-web-screen-shot浏览器截取其他非全屏窗口界面
网页截屏 js-web-screen-shot 截取其他窗口 显示不全问题 npm 安装 js-web-screen-shot npm install js-web-screen-shot --savejs-web-screen-shot默认截屏是从左下角开始的,修改成左上角开始,然后编辑cropBoxInfo参数宽高进行截取,目前截…...
pycharm远程连接服务器运行pytorch
Linux部署pytorch 背景介绍 不同的开源代码可能需要不同的实验环境和版本,这时候的确体现出Anaconda管理环境的好处了,分别搞一个独立环境方便管理。 有的教程建议选择较旧的版本,但笔者建议在条件允许的情况下安装最新版,本次…...
服务器虚拟化是一种将物理服务器资源(如CPU、内存、存储、网络等)通过软件技术抽象、分割和整合,创建多个独立、隔离的虚拟服务器(虚拟机,VM)的技术。
服务器虚拟化是一种将物理服务器资源(如CPU、内存、存储、网络等)通过软件技术抽象、分割和整合,创建多个独立、隔离的虚拟服务器(虚拟机,VM)的技术。每个虚拟机可以运行不同的操作系统和应用程序,如同独立的物理服务器一样工作。 核心思想 资源池化:将物理服务器的硬…...
java练习(41)
ps:题目来自力扣 最接近的三数之和 给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。 返回这三个数的和。 假定每组输入只存在恰好一个解。 import java.util.Arrays;class Solut…...
关于CanvasRenderer.SyncTransform触发调用的机制
1)关于CanvasRenderer.SyncTransform触发调用的机制 2)小游戏Spine裁剪掉帧问题 3)Dedicated Server性能问题 4).mp4视频放入RT进行渲染的性能分析闭坑指南 这是第421篇UWA技术知识分享的推送,精选了UWA社区的热门话题…...
【计算机网络】OSI模型、TCP/IP模型、路由器、集线器、交换机
一、计算机网络分层结构 计算机网络分层结构 指将计算机网络的功能划分为多个层次,每个层次都有其特定的功能和协议,并且层次之间通过接口进行通信。 分层设计的优势: 模块化:各层独立发展(如IPv4→IPv6,…...
PDF扫描档智能方向识别:多模型投票机制的实践测试 救活古典书籍
2025-02-22 20:10物联全栈123 尊敬的诸位!我是一名物联网工程师。关注我,持续分享最新物联网与AI资讯和开发实战。期望与您携手探寻物联网与 AI 的无尽可能 RAG知识库搭建的过程中,扫描档pdf的支持和准确率一直是个大家都不愿主动提起的事情…...
