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

SpringBoot @Async 异步任务与 @Transactional 事务实战

异步任务和事务管理是 SpringBoot 中两个非常重要但又容易出错的特性。这篇把它们的核心用法和常见坑一次讲清楚。

一、@Async 异步任务

1. 开启异步

@SpringBootApplication@EnableAsync// 开启异步任务publicclassSeckillApplication{publicstaticvoidmain(String[]args){SpringApplication.run(SeckillApplication.class,args);}}

2. 配置线程池

@ConfigurationpublicclassAsyncConfig{@Bean("taskExecutor")publicExecutortaskExecutor(){ThreadPoolTaskExecutorexecutor=newThreadPoolTaskExecutor();executor.setCorePoolSize(5);// 核心线程数executor.setMaxPoolSize(10);// 最大线程数executor.setQueueCapacity(100);// 队列容量executor.setKeepAliveSeconds(60);// 空闲线程存活时间executor.setThreadNamePrefix("async-");executor.setWaitForTasksToCompleteOnShutdown(true);executor.setRejectedExecutionHandler(newThreadPoolExecutor.CallerRunsPolicy());executor.initialize();returnexecutor;}}

3. 使用 @Async

@ServicepublicclassSeckillAsyncService{privatestaticfinalLoggerlog=LoggerFactory.getLogger(SeckillAsyncService.class);/** * 异步发送通知 */@Async("taskExecutor")publicvoidsendNotification(LonguserId,Stringmessage){log.info("异步发送通知给用户 {}, 线程: {}",userId,Thread.currentThread().getName());// 模拟耗时操作try{Thread.sleep(2000);}catch(InterruptedExceptione){Thread.currentThread().interrupt();}log.info("通知发送完成");}/** * 异步记录日志 */@Async("taskExecutor")publicvoidrecordLog(Stringoperation,Stringdetail){log.info("记录操作日志: {} - {}",operation,detail);}}

4. @Async 的使用场景

@ServicepublicclassOrderService{@AutowiredprivateSeckillAsyncServiceasyncService;publicOrdercreateOrder(OrderDTOdto){// 1. 主流程:同步创建订单Orderorder=doCreateOrder(dto);// 2. 副流程:异步执行,不影响主流程asyncService.sendNotification(order.getUserId(),"下单成功");asyncService.recordLog("createOrder",order.getOrderNo());returnorder;}}

5. @Async 的返回值

/** * 异步方法可以返回 void 或 Future * 如果需要获取异步结果,使用 Future */@Async("taskExecutor")publicFuture<String>processAsync(LongorderId){try{Thread.sleep(3000);returnAsyncResult.forResult("处理完成: "+orderId);}catch(InterruptedExceptione){returnAsyncResult.forExecutionException(e);}}// 调用Future<String>future=asyncService.processAsync(orderId);Stringresult=future.get();// 阻塞等待结果

二、@Transactional 事务

1. 事务的 ACID 特性

原子性(Atomicity):事务中的所有操作要么全成功,要么全回滚 一致性(Consistency):事务前后数据保持一致 隔离性(Isolation):并发事务互不干扰 持久性(Durability):提交后数据永久保存

2. 基本使用

@ServicepublicclassOrderServiceImplextendsServiceImpl<OrderMapper,Order>implementsOrderService{/** * 创建订单 + 扣减库存 * 两个操作必须在一个事务中,要么都成功,要么都失败 */@Override@Transactional(rollbackFor=Exception.class)publicOrdercreateOrder(OrderDTOdto){// 1. 创建订单Orderorder=newOrder();order.setUserId(dto.getUserId());order.setProductId(dto.getProductId());order.setAmount(dto.getAmount());save(order);// 2. 扣减库存productService.deductStock(dto.getProductId(),1);returnorder;}}

3. 事务传播行为

/** * 事务传播行为:当一个事务方法调用另一个事务方法时,如何处理 */@ServicepublicclassPayService{/** * REQUIRED(默认):支持当前事务,如果不存在则创建新事务 * SUPPORTS:支持当前事务,如果不存在则以非事务方式执行 * REQUIRES_NEW:创建新事务,挂起当前事务 * NESTED:嵌套事务(JDBC 的 Savepoint 机制) * NEVER:以非事务方式执行,如果有事务则抛异常 */@Transactional(propagation=Propagation.REQUIRES_NEW)publicvoidprocessPayment(LongorderId){// 支付处理,独立于调用方的事务}@Transactional(propagation=Propagation.REQUIRES_NEW)publicvoidrefundPayment(LongorderId){// 退款处理,即使主事务回滚也提交}}

4. 事务隔离级别

/** * 事务隔离级别解决并发问题: * 脏读:读到未提交的数据 * 不可重复读:同一事务中两次读到不同数据 * 幻读:同一事务中读到新增的数据 */@ServicepublicclassInventoryService{@Transactional(isolation=Isolation.READ_COMMITTED)publicvoidupdateStock(LongproductId,intquantity){// READ_COMMITTED:MySQL 默认级别,解决脏读// REPEATABLE_READ:解决脏读 + 不可重复读// SERIALIZABLE:解决所有并发问题(性能最差)}}
隔离级别脏读不可重复读幻读
READ UNCOMMITTED
READ COMMITTED
REPEATABLE READ
SERIALIZABLE

三、@Async + @Transactional 组合

1. 异步方法中的事务

/** * 异步方法中的 @Transactional 是独立的 * 调用方的异常不会导致异步方法回滚 */@ServicepublicclassAsyncTransactionService{@Async("taskExecutor")@Transactional(rollbackFor=Exception.class)publicvoidasyncWithTransaction(LongorderId){// 这个事务和调用方的事务是独立的orderMapper.updateStatus(orderId,1);}}

2. 注意事项

/** * ❌ 错误:同一类中的方法调用 @Async 不生效 */@ServicepublicclassOrderService{publicvoidcreateOrder(){// 这里调用的 asyncMethod 不会异步执行// 因为 Spring 的代理机制只在外部调用时生效this.asyncMethod();}@AsyncpublicvoidasyncMethod(){// 不会被异步执行!}}/** * ✅ 正确:注入自身代理 */@ServicepublicclassOrderService{@Autowired@LazyprivateOrderServiceself;publicvoidcreateOrder(){self.asyncMethod();// 通过代理调用,异步生效}@AsyncpublicvoidasyncMethod(){// 异步执行}}

四、秒杀系统中的事务应用

1. 秒杀下单事务

@ServicepublicclassSeckillService{@Override@Transactional(rollbackFor=Exception.class)publicOrderdoSeckill(LongproductId,LonguserId){// 1. 校验库存Productproduct=productService.getById(productId);if(product.getStock()<=0){thrownewBusinessException(400,"库存不足");}// 2. 扣减库存(乐观锁)booleansuccess=productService.deductStock(productId);if(!success){thrownewBusinessException(400,"库存不足");}// 3. 创建订单Orderorder=newOrder();order.setUserId(userId);order.setProductId(productId);order.setStatus(0);save(order);returnorder;}}

2. 异步处理超时订单

@ServicepublicclassOrderTimeoutService{@Async("taskExecutor")@Transactional(rollbackFor=Exception.class)publicvoidcancelTimeoutOrder(LongorderId){// 1. 取消订单Orderorder=getById(orderId);if(order==null||order.getStatus()!=0){return;}order.setStatus(2);// 已取消updateById(order);// 2. 归还库存productService.restoreStock(order.getProductId(),order.getQuantity());}}

五、常见问题

1. 事务不回滚

// ❌ 错误:没有指定 rollbackFor@TransactionalpublicvoidcreateOrder(){save(order);thrownewRuntimeException("失败");// RuntimeException 默认回滚thrownewBusinessException("失败");// 自定义异常默认不回滚!}// ✅ 正确:指定 rollbackFor@Transactional(rollbackFor=Exception.class)publicvoidcreateOrder(){save(order);thrownewBusinessException("失败");// 回滚}

2. 异步方法中的异常处理

@Async("taskExecutor")publicvoidprocessAsync(){try{// 业务逻辑}catch(Exceptione){// 异步方法的异常不会被调用方捕获// 必须自己处理log.error("异步处理失败",e);}}

3. 事务与锁的顺序

/** * 获取锁 → 开启事务 → 执行业务 → 提交事务 → 释放锁 * * 不能先释放锁再提交事务,否则其他线程会读到未提交的数据 */publicvoidseckillWithLock(LongproductId,LonguserId){StringlockKey="lock:seckill:"+productId;StringlockValue=UUID.randomUUID().toString();try{// 1. 获取锁booleanlocked=redisLock.lock(lockKey,lockValue,10);if(!locked){thrownewBusinessException(400,"操作太频繁");}// 2. 事务操作seckillService.doSeckill(productId,userId);// 3. 释放锁(在事务提交之后)}finally{redisLock.unlock(lockKey,lockValue);}}

总结

@Async 用于: 耗时操作(发送邮件、短信通知、生成报表) 不依赖主流程结果的副流程(日志记录、数据分析) 实时性要求不高的批量处理 @Transactional 用于: 多表操作(订单+库存、用户+积分) 需要原子性保证的业务(支付、退款、转账)

💡 觉得有用的话,点赞 + 关注【张老师技术栈】吧!每周更新 Java/Python/爬虫 实战干货,不让你白来。

http://www.cnnetsun.cn/news/3353935.html

相关文章:

  • ADK 10.1.26100.2454 部署 Win11PE:7个必备组件包添加与中文环境配置实战
  • SAP MM 自动记账配置详解:OBYC 与 5 种常见业务场景科目确定
  • VS Code 1.89 + MinGW-w64 GCC 13.2 配置:3步解决C++20标准支持与IntelliSense
  • 工业负载控制:TPD2015FN与PIC18F87J10的智能驱动方案
  • 51单片机--通信/串口
  • COSBench 0.4.2.c4 分布式压测配置:3节点Driver部署与性能对比分析
  • Altium Designer 原理图转PCB避坑指南:从ECO到布局的3个关键检查点
  • AI时代如何逆袭?这份「收藏级」指南助你抢占大模型风口!
  • 电商大促期间数据量暴增,人工扛不住怎么办?——企业级AI Agent自动化破局全指南
  • 3个关键参数解析:Chromatix AEC 曝光表与 Sensor 帧率/行时计算
  • AI 电动植物纤维餐具热封机智能功率 MOSFET 完整选型方案
  • 5.1 市场是一个噪声源,而你在裸采
  • 2026本科生刷专业学术网课 超实用的学术视频总结方法整理
  • PIC18F4525与CMT-8540S-SMT嵌入式音频方案解析
  • 2026年注浆泵厂家选型参考:从产品矩阵、技术路径到适用场景的客观梳理
  • NAU8224与PIC32MZ音频系统设计与优化实战
  • 【AI智能问数】数据二十条+数字中国:政策驱动下的AI问数机遇
  • Claude多语言翻译能力全解析,从零基础到专业级调优的7个关键参数(工程师内部手册首次公开)
  • Claude API构建Shopify智能客服与退货自动化系统实战
  • 开源项目第158期:cangjie-skill — 把书、视频、播客里的方法论蒸馏成可调用的 AI Skills
  • Cesium for Unreal 1.22.0实战:三维GIS与虚幻引擎融合指南
  • 用Rokid AI眼镜做家庭药箱助手:拍照识药、语音补全与永久药箱的Agent实践
  • Markdown编辑器快速使用方法
  • SQL准确率从68%飙升至99.2%:我们用17类典型业务场景验证ChatGPT的语法、语义与性能三重校准法
  • 「实战应用」如何用图表控件LightningChart JS创建SQL仪表板应用(三)
  • 【小程序毕业设计】校园社团招新与活动发布小程序系统的设计与实现 高校社团日常运维与成员管理系统的设计与实现(源码+文档+远程调试,全bao定制等)
  • 怎样轻松保存网络视频?Video Download Helper免费插件实用指南
  • PG 日报|新增 VFD 缓存监控视图,精细化数据库调优
  • 2026勰码金牌夏令营济南A-营模拟赛一补题报告
  • ABAP 里没有 Angular Subscription 的同名对象,但有更贴近事务边界的资源释放方式