
异步任务和事务管理是 SpringBoot 中两个非常重要但又容易出错的特性。这篇把它们的核心用法和常见坑一次讲清楚。一、Async 异步任务1. 开启异步SpringBootApplicationEnableAsync// 开启异步任务publicclassSeckillApplication{publicstaticvoidmain(String[]args){SpringApplication.run(SeckillApplication.class,args);}}2. 配置线程池ConfigurationpublicclassAsyncConfig{Bean(taskExecutor)publicExecutortaskExecutor(){ThreadPoolTaskExecutorexecutornewThreadPoolTaskExecutor();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. 使用 AsyncServicepublicclassSeckillAsyncService{privatestaticfinalLoggerlogLoggerFactory.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. 主流程同步创建订单OrderorderdoCreateOrder(dto);// 2. 副流程异步执行不影响主流程asyncService.sendNotification(order.getUserId(),下单成功);asyncService.recordLog(createOrder,order.getOrderNo());returnorder;}}5. Async 的返回值/** * 异步方法可以返回 void 或 Future * 如果需要获取异步结果使用 Future */Async(taskExecutor)publicFutureStringprocessAsync(LongorderId){try{Thread.sleep(3000);returnAsyncResult.forResult(处理完成: orderId);}catch(InterruptedExceptione){returnAsyncResult.forExecutionException(e);}}// 调用FutureStringfutureasyncService.processAsync(orderId);Stringresultfuture.get();// 阻塞等待结果二、Transactional 事务1. 事务的 ACID 特性原子性Atomicity事务中的所有操作要么全成功要么全回滚 一致性Consistency事务前后数据保持一致 隔离性Isolation并发事务互不干扰 持久性Durability提交后数据永久保存2. 基本使用ServicepublicclassOrderServiceImplextendsServiceImplOrderMapper,OrderimplementsOrderService{/** * 创建订单 扣减库存 * 两个操作必须在一个事务中要么都成功要么都失败 */OverrideTransactional(rollbackForException.class)publicOrdercreateOrder(OrderDTOdto){// 1. 创建订单OrderordernewOrder();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(propagationPropagation.REQUIRES_NEW)publicvoidprocessPayment(LongorderId){// 支付处理独立于调用方的事务}Transactional(propagationPropagation.REQUIRES_NEW)publicvoidrefundPayment(LongorderId){// 退款处理即使主事务回滚也提交}}4. 事务隔离级别/** * 事务隔离级别解决并发问题 * 脏读读到未提交的数据 * 不可重复读同一事务中两次读到不同数据 * 幻读同一事务中读到新增的数据 */ServicepublicclassInventoryService{Transactional(isolationIsolation.READ_COMMITTED)publicvoidupdateStock(LongproductId,intquantity){// READ_COMMITTEDMySQL 默认级别解决脏读// REPEATABLE_READ解决脏读 不可重复读// SERIALIZABLE解决所有并发问题性能最差}}隔离级别脏读不可重复读幻读READ UNCOMMITTED✅✅✅READ COMMITTED❌✅✅REPEATABLE READ❌❌✅SERIALIZABLE❌❌❌三、Async Transactional 组合1. 异步方法中的事务/** * 异步方法中的 Transactional 是独立的 * 调用方的异常不会导致异步方法回滚 */ServicepublicclassAsyncTransactionService{Async(taskExecutor)Transactional(rollbackForException.class)publicvoidasyncWithTransaction(LongorderId){// 这个事务和调用方的事务是独立的orderMapper.updateStatus(orderId,1);}}2. 注意事项/** * ❌ 错误同一类中的方法调用 Async 不生效 */ServicepublicclassOrderService{publicvoidcreateOrder(){// 这里调用的 asyncMethod 不会异步执行// 因为 Spring 的代理机制只在外部调用时生效this.asyncMethod();}AsyncpublicvoidasyncMethod(){// 不会被异步执行}}/** * ✅ 正确注入自身代理 */ServicepublicclassOrderService{AutowiredLazyprivateOrderServiceself;publicvoidcreateOrder(){self.asyncMethod();// 通过代理调用异步生效}AsyncpublicvoidasyncMethod(){// 异步执行}}四、秒杀系统中的事务应用1. 秒杀下单事务ServicepublicclassSeckillService{OverrideTransactional(rollbackForException.class)publicOrderdoSeckill(LongproductId,LonguserId){// 1. 校验库存ProductproductproductService.getById(productId);if(product.getStock()0){thrownewBusinessException(400,库存不足);}// 2. 扣减库存乐观锁booleansuccessproductService.deductStock(productId);if(!success){thrownewBusinessException(400,库存不足);}// 3. 创建订单OrderordernewOrder();order.setUserId(userId);order.setProductId(productId);order.setStatus(0);save(order);returnorder;}}2. 异步处理超时订单ServicepublicclassOrderTimeoutService{Async(taskExecutor)Transactional(rollbackForException.class)publicvoidcancelTimeoutOrder(LongorderId){// 1. 取消订单OrderordergetById(orderId);if(ordernull||order.getStatus()!0){return;}order.setStatus(2);// 已取消updateById(order);// 2. 归还库存productService.restoreStock(order.getProductId(),order.getQuantity());}}五、常见问题1. 事务不回滚// ❌ 错误没有指定 rollbackForTransactionalpublicvoidcreateOrder(){save(order);thrownewRuntimeException(失败);// RuntimeException 默认回滚thrownewBusinessException(失败);// 自定义异常默认不回滚}// ✅ 正确指定 rollbackForTransactional(rollbackForException.class)publicvoidcreateOrder(){save(order);thrownewBusinessException(失败);// 回滚}2. 异步方法中的异常处理Async(taskExecutor)publicvoidprocessAsync(){try{// 业务逻辑}catch(Exceptione){// 异步方法的异常不会被调用方捕获// 必须自己处理log.error(异步处理失败,e);}}3. 事务与锁的顺序/** * 获取锁 → 开启事务 → 执行业务 → 提交事务 → 释放锁 * * 不能先释放锁再提交事务否则其他线程会读到未提交的数据 */publicvoidseckillWithLock(LongproductId,LonguserId){StringlockKeylock:seckill:productId;StringlockValueUUID.randomUUID().toString();try{// 1. 获取锁booleanlockedredisLock.lock(lockKey,lockValue,10);if(!locked){thrownewBusinessException(400,操作太频繁);}// 2. 事务操作seckillService.doSeckill(productId,userId);// 3. 释放锁在事务提交之后}finally{redisLock.unlock(lockKey,lockValue);}}总结Async 用于 耗时操作发送邮件、短信通知、生成报表 不依赖主流程结果的副流程日志记录、数据分析 实时性要求不高的批量处理 Transactional 用于 多表操作订单库存、用户积分 需要原子性保证的业务支付、退款、转账 觉得有用的话点赞 关注【张老师技术栈】吧每周更新 Java/Python/爬虫 实战干货不让你白来。