29小节:oneThread与普通线程池性能测试
作者:程序员马丁
热门项目实战社群,收获国内众多知名公司面试青睐,近千名同学面试成功!助力你在校招或社招上拿个offer。
oneThread与普通线程池性能测试,元数据信息:
- 什么是线程池oneThread:https://t.zsxq.com/5GfrN
- 代码仓库:https://gitcode.net/nageoffer/onethread —— 申请项目权限参考上述线程池项目链接
- 章节难度:★★☆☆☆ - 中等
- 视频地址:本章节内容简单,无
©版权所有 - 拿个offer-开源&项目实战星球专属学习项目,依据《中华人民共和国著作权法实施条例》和《知识星球产权保护》,严禁未经本项目原作者明确书面授权擅自分享至 GitHub、Gitee 等任何开放平台。违者将面临法律追究。
内容摘要:我们为了实现动态调整、监控告警这些功能,在原生线程池的基础上增加了不少逻辑,这些额外的开销到底有多大?会不会影响到线程池本身的性能?
课程目录如下所示:
- 前言
- 动态线程池增强功能
- 设计测试方案
- 性能测试结论
前言
前段时间星球上有位同学在面试中,被问到:今天面试时被问到动态线程池和普通线程池的性能有没有区别?
带着这个疑问,我决定做一次完整的性能测试。这篇文章会记录整个测试过程,包括如何设计测试方案、如何分析性能瓶颈,以及最后得出的结论。
动态线程池增强功能
在开始测试之前,我们需要搞清楚动态线程池相比普通 线程池到底多了哪些东西。
1. 监控采集
oneThread 实现了一个 ThreadPoolMonitor,每隔指定时间就会采集一次线程池的运行数据:
private void micrometerMonitor(ThreadPoolRuntimeInfo runtimeInfo) {
// 采集核心指标
Metrics.gauge(metricName("core.size"), tags, registerRuntimeInfo,
ThreadPoolRuntimeInfo::getCorePoolSize);
Metrics.gauge(metricName("active.size"), tags, registerRuntimeInfo,
ThreadPoolRuntimeInfo::getActivePoolSize);
// ... 还有很多其他指标
}
这里有个细节需要注意:getActiveCount()、getPoolSize() 这些方法在 JDK 源码里是加锁的。翻了下 ThreadPoolExecutor 的源码:
public int getActiveCount() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock(); // 这里会加锁!
try {
int n = 0;
for (Worker w : workers)
if (w.isLocked())
++n;
return n;
} finally {
mainLock.unlock();
}
}
如果监控采集频率太高,这个锁竞争可能会成为性能瓶颈。
2. 告警检查
ThreadPoolAlarmChecker 会定期检查队列使用率、线程活跃度、拒绝次数:
private void checkAlarm() {
Collection<ThreadPoolExecutorHolder> holders = OneThreadRegistry.getAllHolders();
for (ThreadPoolExecutorHolder holder : holders) {
if (holder.getExecutorProperties().getAlarm().getEnable()) {
checkQueueUsage(holder); // 检查队列
checkActiveRate(holder); // 检查活跃度
checkRejectCount(holder); // 检查拒绝次数
}
}
}
每次检查都要调用那些带锁的 API,这也是潜在的性能开销点。
3. 拒绝策略包装
为了统计拒绝次数,我用 Lambda 包装了原始的拒绝策略:
@Override
public void setRejectedExecutionHandler(RejectedExecutionHandler handler) {
RejectedExecutionHandler handlerWrapper = new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
rejectCount.incrementAndGet(); // 计数
handler.rejectedExecution(r, executor);
}
};
super.setRejectedExecutionHandler(handlerWrapper);
}
虽然只是简单的计数器递增,但每次拒绝都会多执行一次方法调用。
4. 整体架构
用 PlantUML 画个图,看看这些组件是怎么协作的:

从图上可以看出,监控和告警模块都是通过定时任务独立运行的,理论上不应该影响业务线程提交任务的性能。但实际情况如何,还得测了才知道。
动态变更仅涉及到异步更新,且对线程池运行不影响,因此不在整体架构范畴。
设计测试方案
1. 为什么不用 JMH?
一开始我想用 JMH 做基准测试,但很快发现了问题:JMH 是独立运行的,根本没有 Spring 容器,那些监控和告警的定时任务压根就 没启动。
测出来的结果肯定不准确,因为我们要测的就是监控和告警对性能的影响。
2. 基于 SpringBoot 压测方案
性能测试不能拍脑袋,得有科学的方法。既然动态线程池依赖 Spring 环境,那就直接在 Spring Boot 测试类里模拟压力。

3. 实现测试代码
在进行线程池性能对比测试时,我们设计了一个系统化的流程,以确保结果的准确性和可重复性。
该测试主要针对普通线程池(固定线程配置)和动态线程池(例如 onethreadProducer)在不同负载场景下的表现,包括轻负载、中负载和高负载。
整个过程通过 Spring Boot 的 REST API 端点触发(如 /test/light-load),并输出详细的性能指标对比。下面,我将逐步拆解这个测试流程,理解如何在实际项目中应用类似基准测试。