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

摘要: 原创出处 juejin.cn/post/7533124239526477866 「天天摸鱼的java工程师」欢迎转载,保留摘要,谢谢!


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

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

一、背景介绍:什么场景下会遇到这种问题?

在一次处理大促活动中的任务系统时,我们遇到了这样的需求:

  • 接口吞吐量:QPS 高达 100,000(10 万)
  • 每个任务处理平均耗时:100ms
  • 每个请求都需要进线程池执行处理逻辑(比如发通知、写日志、异步落库等)
  • 要求整体系统稳定、响应快、资源利用率高

如果不加优化,线程池很容易撑爆 CPU 和内存,甚至引发系统雪崩。

二、问题分析:常规线程池会怎样?

举个例子

如果我们不做优化,简单使用 Java 默认线程池配置:

Executors.newFixedThreadPool(100);

面对 10 万 QPS 的请求,每个任务耗时 100ms:

  • 每秒需要处理 100000 个任务
  • 每个任务需要 100ms,也就是每个线程每秒最多处理 10 个请求
  • 所以至少需要:100000 / 10 = 10000 个线程才能撑住!

👿 问题:Java 虚拟机会因创建大量线程而崩溃(线程上下文切换开销巨大,内存压力爆炸)

三、优化目标

我们要实现的目标是:

  • 控制线程数,避免线程爆炸
  • 提升吞吐能力,让单个线程处理更多任务
  • 合理使用队列和限流,避免系统雪崩
  • 提供平稳退化机制:拒绝策略、降级处理

四、解决方案:线程池优化策略

✅ 核心策略如下:

策略 说明
任务批处理 100ms内合并任务批量执行,提升效率
合理配置线程池参数 核心线程数、最大线程数、队列大小
使用异步化 + 限流 限制入池速率,保护线程池不被打爆
使用自定义拒绝策略 打印日志、降级处理、放入备用队列等

五、代码实现:线程池优化实战(含注释)

我们以 Spring Boot + 自定义线程池为例,构建一个高性能任务处理系统。

1. 线程池配置类

@Configuration
publicclassThreadPoolConfig {

@Bean("taskExecutor")
public ThreadPoolExecutor taskExecutor() {
intcorePoolSize=200; // 核心线程数
intmaxPoolSize=500; // 最大线程数
intqueueCapacity=10000; // 队列容量
intkeepAliveTime=60; // 非核心线程最大空闲时间(秒)

returnnewThreadPoolExecutor(
corePoolSize,
maxPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
newLinkedBlockingQueue<>(queueCapacity),
newThreadFactory() {
privatefinalAtomicIntegercount=newAtomicInteger(0);

@Override
public Thread newThread(Runnable r) {
returnnewThread(r, "task-executor-" + count.getAndIncrement());
}
},
newRejectedExecutionHandler() {
@Override
publicvoidrejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// 自定义拒绝策略:记录日志 + 降级处理
System.err.println("任务被拒绝执行:" + r.toString());
// 可以添加报警、入备用队列等逻辑
}
}
);
}
}

2. 业务层异步任务提交

@Service
publicclassTaskService {

@Resource(name = "taskExecutor")
private ThreadPoolExecutor taskExecutor;

/**
* 接收高并发请求,异步处理任务
* @param taskId 任务ID
*/
publicvoidsubmitTask(String taskId) {
taskExecutor.execute(() -> {
longstart= System.currentTimeMillis();
try {
// 模拟任务耗时处理
Thread.sleep(100); // 实际场景可为远程调用、数据库操作等

System.out.println(Thread.currentThread().getName()
+ " 执行任务 [" + taskId + "] 成功,耗时:" +
(System.currentTimeMillis() - start) + "ms");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
}

3. Controller 接口模拟压测

@RestController
@RequestMapping("/api/task")
public class TaskController {

@Autowired
private TaskService taskService;

/**
* 模拟高并发任务提交接口
* @param taskId 任务ID
*/
@PostMapping("/submit")
public ResponseEntity<String> submit(@RequestParam String taskId) {
taskService.submitTask(taskId);
return ResponseEntity.ok("任务已提交");
}
}

六、进阶优化建议

1. 使用 Disruptor 或 LMAX RingBuffer 替代线程池

对于毫秒级、极限吞吐量场景,可以使用 Disruptor(单线程+无锁队列),吞吐量可达百万 QPS。

2. 异步合并 + 批处理

例如每 100ms 收集一批任务,一次性处理:

// 使用 ScheduledExecutorService 定时拉取任务批处理,提高 CPU 利用率,减少线程切换

// 或者使用 RxJava/Project Reactor 等响应式框架处理

3. 限流 + 熔断 + 降级

利用 Sentinel 或 Resilience4j 对接口加限流、熔断保护:

// 限流策略:如每秒只允许 10w 请求进入线程池
// 熔断策略:连续失败时自动拒绝请求,避免故障蔓延

七、总结

面对 QPS 10 万、任务耗时 100ms 的高并发场景,线程池优化是重中之重。我们从业务特性出发,结合线程池的核心参数配置,设计了合理的任务分发机制。

优化线程池的关键在于:限制并发、提升单线程效率、合理拒绝请求、异步批处理。

文章目录
  1. 1. 一、背景介绍:什么场景下会遇到这种问题?
  2. 2. 二、问题分析:常规线程池会怎样?
    1. 2.0.0.1. 举个例子
  • 3. 三、优化目标
  • 4. 四、解决方案:线程池优化策略
    1. 4.0.0.1. ✅ 核心策略如下:
  • 5. 五、代码实现:线程池优化实战(含注释)
    1. 5.0.1. 1. 线程池配置类
    2. 5.0.2. 2. 业务层异步任务提交
    3. 5.0.3. 3. Controller 接口模拟压测
  • 6. 六、进阶优化建议
    1. 6.0.1. 1. 使用 Disruptor 或 LMAX RingBuffer 替代线程池
    2. 6.0.2. 2. 异步合并 + 批处理
    3. 6.0.3. 3. 限流 + 熔断 + 降级
  • 7. 七、总结