⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 blog.csdn.net/2302_80633404 「kkkkkkkkl24」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

一、背景

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 线程池并非万能

  • 仍需配合限流、熔断等机制
  • 核心链路与非核心链路应区别对待
文章目录
  1. 1. 一、背景
    1. 1.0.1. 1.1 业务背景
    2. 1.0.2. 1.2 问题描述
    3. 1.0.3. 1.3 技术挑战
  • 2. 二、业务场景分析与并行拆分
    1. 2.0.1. 2.1 订单创建流程拆解
    2. 2.0.2. 2.2 并行化设计思路
  • 3. 三、技术选型与整体设计
    1. 3.0.1. 3.1 为什么不直接 new Thread?
    2. 3.0.2. 3.2 为什么选择线程池 + CompletableFuture?
    3. 3.0.3. 3.3 在 Spring Boot 中的正确姿势
  • 4. 四、线程池设计与配置(Core)
    1. 4.0.1. 4.1 线程池配置代码
    2. 4.0.2. 4.2 关键参数说明
    3. 4.0.3. 4.3 线程池定位
  • 5. 五、核心业务实现
    1. 5.0.1. 5.1 校验 Service(模拟 RPC / DB)
    2. 5.0.2. 5.2 订单创建核心逻辑
    3. 5.0.3. 5.3 Controller
  • 6. 六、JMeter 压测与结果分析
    1. 6.0.1. 6.1 压测配置说明
    2. 6.0.2. 6.2 压测现象
    3. 6.0.3. 6.3 原因分析
    4. 6.0.4. 6.4 工程结论
  • 7. 七、问题思考
    1. 7.0.1. 7.1 为什么不用 @Async?
    2. 7.0.2. 7.2 为什么不用 parallelStream?
    3. 7.0.3. 7.3 线程池并非万能