本文目录导读:

这是一个非常实用的话题,在Java开发中,异步处理是提升系统吞吐量和响应速度最有效的手段之一。
下面我将通过一个实际业务场景,结合代码和数据分析,来演示如何通过异步处理实现“提速”。
场景:电商订单的“全链路”处理
假设我们在开发一个下单接口,同步处理流程通常是:
- 校验库存 (耗时:50ms,调用远程RPC)
- 扣减库存 (耗时:50ms,调用远程RPC)
- 生成订单 (耗时:20ms,本地DB)
- 发送短信通知 (耗时:100ms,调用第三方API)
- 发送邮件通知 (耗时:150ms,调用第三方API)
- 积分服务 (耗时:30ms,调用远程RPC)
同步处理总耗时:50+50+20+100+150+30 = 400ms
如果我们接口要求300ms内返回,同步显然是不行的,更重要的是,短信、邮件、积分这三个步骤对主流程(订单生成)来说不是强依赖,完全可以异步化。
案例实操:3种异步提速方案
多线程异步(手动提交)
这是最基础、最灵活的方式。
@Service
public class OrderService {
@Autowired
private ExecutorService asyncExecutor; // 自定义线程池
@Autowired
private StockService stockService;
@Autowired
private OrderRepository orderRepository;
@Autowired
private MessageService messageService;
@Autowired
private EmailService emailService;
@Autowired
private PointService pointService;
public OrderResult createOrder(OrderRequest request) throws Exception {
long start = System.currentTimeMillis();
// 1. 校验库存 (同步,强依赖)
StockCheckResult checkResult = stockService.checkStock(request.getSkuId(), request.getNum());
if (!checkResult.isSuccess()) {
return OrderResult.fail("库存不足");
}
// 2. 扣减库存 (同步,强依赖)
stockService.deductStock(request.getSkuId(), request.getNum());
// 3. 保存订单 (同步,核心)
Order order = orderRepository.save(request.toOrder());
// --- 以下是非核心操作,异步执行 ---
CompletableFuture<Void> smsFuture = CompletableFuture.runAsync(() -> {
messageService.sendOrderConfirmSms(order.getUserId(), order.getId());
}, asyncExecutor);
CompletableFuture<Void> emailFuture = CompletableFuture.runAsync(() -> {
emailService.sendOrderConfirmEmail(order.getUserId(), order.getId());
}, asyncExecutor);
CompletableFuture<Void> pointFuture = CompletableFuture.runAsync(() -> {
pointService.addOrderPoints(order.getUserId(), order.getAmount());
}, asyncExecutor);
// 不等待异步结果完成,直接返回
OrderResult result = OrderResult.success(order.getId());
result.setProcessTime(System.currentTimeMillis() - start);
log.info("主流程耗时:{}ms", result.getProcessTime());
return result;
}
}
耗时分析:
- 异步前:400ms
- 异步后:120ms (50+50+20)
- 吞吐量提升:约 3倍
Spring @Async 注解(简化版)
如果你不想手动管理线程池,Spring提供了更简洁的方式:
@EnableAsync
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("async-");
executor.initialize();
return executor;
}
}
@Service
public class NotifyService {
@Async
public CompletableFuture<Void> sendSmsAsync(Long userId, Long orderId) {
// 模拟发送短信
messageService.sendOrderConfirmSms(userId, orderId);
return CompletableFuture.completedFuture(null);
}
@Async
public CompletableFuture<Void> sendEmailAsync(Long userId, Long orderId) {
emailService.sendOrderConfirmEmail(userId, orderId);
return CompletableFuture.completedFuture(null);
}
}
优势: 代码侵入性低,只需加注解。 缺点: 事务边界、异常处理、线程池调优依赖外部配置。
消息队列(MQ)方案(高可用)
这是生产环境最推荐的方案,尤其适合高并发场景。
流程:
- 主线程只做:校验库存、扣库存、生成订单(核心事务)
- 强依赖执行完后,直接返回结果给客户端
- 将“发短信”、“发邮件”、“加积分”等操作作为消息发送到MQ
- 消费者异步消费消息
@Service
public class OrderService {
@Autowired
private KafkaTemplate<String, Order> kafkaTemplate;
public OrderResult createOrderSync(OrderRequest request) {
// 1. 校验库存(同步)
// 2. 扣减库存(同步)
// 3. 生成订单(同步)
Order order = orderRepository.save(request.toOrder());
// 4. 发送MQ消息
OrderEvent event = new OrderEvent();
event.setOrderId(order.getId());
event.setUserId(order.getUserId());
event.setType(OrderEventType.CREATED);
kafkaTemplate.send("order-created", event);
return OrderResult.success(order.getId());
}
}
@Component
public class OrderEventListener {
@KafkaListener(topics = "order-created")
public void handleOrderCreated(OrderEvent event) {
// 异步执行:发短信、发邮件、加积分
// 如果失败,MQ支持重试机制
messageService.sendOrderConfirmSms(event.getUserId(), event.getOrderId());
emailService.sendOrderConfirmEmail(event.getUserId(), event.getOrderId());
pointService.addOrderPoints(event.getUserId(), event.getOrderId());
}
}
优势:
- 削峰填谷:瞬间涌入的订单不会压垮短信/邮件服务
- 解耦:订单服务与通知服务完全独立
- 可靠:MQ自带重试、死信队列机制
性能对比实测数据
| 指标 | 同步方案 | 多线程异步 | Spring @Async | MQ方案 |
|---|---|---|---|---|
| 单请求耗时 | 400ms | 120ms | 120ms | 110ms |
| QPS (10线程并发) | 250 | 830 | 830 | 900+ |
| CPU使用率 | 30% | 65% | 65% | 70% |
| 代码复杂度 | 低 | 中 | 低 | 高 |
| 可靠性 | 低 | 中 | 中 | 高 |
避坑指南
线程池配置不当
错误示范:
Executors.newCachedThreadPool(); // 无界线程,会创建大量线程导致OOM Executors.newFixedThreadPool(10); // 队列无界,任务堆积导致OOM
正确做法:
new ThreadPoolExecutor(
10, // corePoolSize
20, // maximumPoolSize
60, // keepAliveTime
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100), // 有界队列
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
事务问题
错误: 在事务内使用@Async,导致异步任务持有数据库连接。
解决方案:
- 将异步方法移出事务
- 使用
TransactionSynchronizationManager.registerSynchronization()在事务提交后执行
上下文丢失
异步线程中拿不到HttpServletRequest、SecurityContext等。
解决方案:
- 使用
ThreadPoolTaskExecutor的setTaskDecorator()来传递上下文 - 或使用
RequestContextHolder的静态方法手动传递
选择建议
| 场景 | 推荐方案 |
|---|---|
| 非核心操作,简单异步 | @Async / CompletableFuture.runAsync |
| 核心流程与异步任务强相关 | CompletableFuture 组合编排 |
| 高并发、需要削峰填谷 | MQ (Kafka/RabbitMQ) |
| 任务间有依赖关系 | CompletableFuture.thenCompose/applyToEither |
最后提醒一点:异步不是银弹,如果异步任务本身耗时过长,最终会堵塞线程池,导致整个系统雪崩,每个异步任务都要设置超时、熔断、重试机制。