一、背景
1.1 业务背景
以电商系统「订单创建」接口为例
一个用户下单请求,往往需要完成多个业务步骤:
校验库存
校验用户信息
计算订单价格
锁库存
创建订单
1.2 问题描述
传统实现方式:串行执行
在高并发场景下:
接口 RT 高
线程被长时间占用
系统吞吐下降
1.3 技术挑战
哪些任务可以并行?
如何安全、高效地并行?
如何在 Spring Boot 中正确使用线程池?
二、业务场景分析与并行拆分
2.1 订单创建流程拆解
步骤 是否存在依赖 是否可并行 校验库存 无 ✅ 校验用户 无 ✅ 计算价格 无 ✅ 锁库存 依赖库存校验 ❌ 创建订单 依赖前置结果 ❌ 2.2 并行化设计思路
无依赖的校验类任务 → 并行
存在业务依赖的核心流程 → 串行
线程池只用于短生命周期任务
三、技术选型与整体设计
3.1 为什么不直接 new Thread?
线程创建成本高
无法控制并发量
高并发下容易导致 JVM 失控
3.2 为什么选择线程池 + CompletableFuture?
线程复用,降低系统开销
明确的并发上限
支持任务编排(allOf / thenCombine)
3.3 在 Spring Boot 中的正确姿势
线程池必须交由 Spring 管理
使用
ThreadPoolTaskExecutor为业务定制专用线程池,避免互相影响
四、线程池设计与配置(Core)
4.1 线程池配置代码
@Configuration public class OrderThreadPoolConfig { @Bean("orderExecutor") public ThreadPoolTaskExecutor orderExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(8); executor.setMaxPoolSize(16); executor.setQueueCapacity(200); executor.setKeepAliveSeconds(60); executor.setThreadNamePrefix("order-create-"); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; } }4.2 关键参数说明
corePoolSize:常态并发能力maxPoolSize:应对突发流量queueCapacity:缓冲任务,防止雪崩RejectedExecutionHandler:选择
CallerRunsPolicy实现自然限流
4.3 线程池定位
Web 请求内使用
IO + 轻计算混合型线程池
非长任务、非阻塞型任务
五、核心业务实现
5.1 校验 Service(模拟 RPC / DB)
@Service public class OrderCheckService { public boolean checkStock(Long skuId) { sleep(100); return true; } public boolean checkUser(Long userId) { sleep(80); return true; } public int calcPrice(Long skuId) { sleep(120); return 99; } private void sleep(long ms) { try { Thread.sleep(ms); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }5.2 订单创建核心逻辑
@Service public class OrderService { @Resource(name = "orderExecutor") private Executor orderExecutor; @Autowired private OrderCheckService orderCheckService; public String createOrder(Long userId, Long skuId) throws Exception { long start = System.currentTimeMillis(); CompletableFuture<Boolean> stockFuture = CompletableFuture.supplyAsync( () -> orderCheckService.checkStock(skuId), orderExecutor); CompletableFuture<Boolean> userFuture = CompletableFuture.supplyAsync( () -> orderCheckService.checkUser(userId), orderExecutor); CompletableFuture<Integer> priceFuture = CompletableFuture.supplyAsync( () -> orderCheckService.calcPrice(skuId), orderExecutor); CompletableFuture.allOf( stockFuture, userFuture, priceFuture).join(); if (!stockFuture.get()) { throw new RuntimeException("库存不足"); } int price = priceFuture.get(); lockStock(skuId); saveOrder(userId, skuId, price); return "success, cost=" + (System.currentTimeMillis() - start) + "ms"; } private void lockStock(Long skuId) { sleep(50); } private void saveOrder(Long userId, Long skuId, int price) { sleep(80); } private void sleep(long ms) { try { Thread.sleep(ms); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }5.3 Controller
@RestController @RequestMapping("/order") public class OrderController { @Autowired private OrderService orderService; @PostMapping("/create") public String createOrder( @RequestParam Long userId, @RequestParam Long skuId) throws Exception { return orderService.createOrder(userId, skuId); } }六、JMeter 压测与结果分析
6.1 压测配置说明
并发线程数:200
Ramp-Up:1 秒
6.2 压测现象
系统启动初期:
接口 RT ≈288ms
持续压测后:
RT 逐渐上升
峰值约1888ms
6.3 原因分析
每个请求会向线程池提交3 个并行任务
200 个并发请求 ≈600 个线程池任务
线程池最大并发执行数为16
多余任务进入阻塞队列
队列满后触发
CallerRunsPolicy部分任务由HTTP 工作线程执行,导致请求处理时间变长
6.4 工程结论
RT 上升并不代表线程池失效
这是线程池在高并发下的自我保护行为
相比无限创建线程导致系统崩溃,RT 变慢是一种可接受的退化方式
线程池优化的是系统吞吐与稳定性,而不是在无限并发下保持恒定响应时间。
七、问题思考
7.1 为什么不用 @Async?
难以进行复杂任务编排
不利于精细化控制线程池
7.2 为什么不用 parallelStream?
使用公共 ForkJoinPool
线程资源不可控
7.3 线程池并非万能
仍需配合限流、熔断等机制
核心链路与非核心链路应区别对待