当前位置: 首页 > 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.jsqmd.com/news/1183445/

相关文章:

  • Frp vs Ngrok vs NPS:3款内网穿透工具在安全测试中的横向对比
  • 2026深圳电缆回收按吨还是按米划算?行业数据与正规选型全解析 - 再生资源回收资讯
  • 2026年07月 哈尔滨市悦源钢结构彩板有限公司——聚焦东北寒冷气候的钢结构与彩板定制化服务商解析 - 甄选服务推荐
  • Rancher 2.6.4 监控配置实战:3步创建 ServiceMonitor 监控 Java 应用 JMX 指标
  • 【大数据毕业设计】基于 SpringBoot + 小程序的运动数据记录与分析系统的设计与实现(源码+文档+远程调试,全bao定制等)
  • C++微服务通信:五种高效消息传递方案深度解析与实战选型
  • 图同构判定算法 C++ 实现:邻接矩阵全排列法 O(n²×n!) 复杂度分析
  • 2026年7月寄快递上门取件哪个比较便宜?热门平台深度测评与避坑指南 - 快递物流资讯
  • 2026年7月最新南通雅典官方售后客服服务电话及地址网点大全 - 亨得利官方服务中心
  • PlantUML 工具实战:5分钟自动生成UML类图与序列图
  • Skill 技能系统完全指南(五):实战 Skill——代码质量
  • 2026评测 宁波附近大型不锈钢滑滑梯定制厂家选购参考 - 起跑123
  • Cheat Engine 7.5 指针扫描实战:3步定位游戏动态地址,从13万筛选到1个
  • 天祝卖黄金必看!华藏寺黄金回收套路全拆解,3 家本地合规门店无隐形扣费(2026 本地实测) - 福金阁黄金回收
  • 2026年7月最新济南宇舶官方售后热线及客户服务网点地址 - 亨得利官方服务中心
  • 第 001 集:OpenCL 入门导论 - 并行计算时代与异构计算概览
  • 2026 年新消息:广灵优秀的工业污水固液脱水机制造企业推荐几家,告别高成本:这台设备如何让污水处理成本骤降? - 行业推荐官【认证】
  • Grok 4.5 震撼发布!马斯克最强 Agent 暴击 Claude/GPT:不仅是速度,更是开发者的生产力革命!(附免费通道)
  • 数据科学从业者防脱轨手册:降低认知负荷的实战方法论
  • ROS Melodic 与 SocketCAN 方案对比:基于 CANalyst-II 库的 3 种通信架构性能分析
  • 枣庄山亭黄金回收门店实测测评!本地 3 家正规实体回收铺对比,变现旧金不踩坑 - 福金阁黄金回收
  • 20260713 4+5 41 8
  • C++从零实现动态四叉树:空间索引与碰撞检测优化实践
  • 2026走访食品包材车间了解水电气供料系统服务商近况 - 起跑123
  • Altium Designer 封装设计3大常见误区:以100mil网格与3D模型匹配为例
  • ALU 性能演进史:从 74181 4位芯片到现代 CPU 多核并行计算的 50 年变迁
  • Maven 常用命令详解:从入门到精通
  • 中央净水器小户型2026推荐,适配小空间的实用之选 - 米諾
  • 5G NSA ENDC 部署实战:MCG/SCG 配置与 3 种承载类型详解
  • GPT image2提示词实战指南:20组模板与参数优化技巧