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

摘要: 原创出处 小哈学Java 「犬小哈」欢迎转载,保留摘要,谢谢!


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

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

这道题真正在筛什么人

面试官不是想听你说“200”这个数字。他想知道的是:

  1. 你能不能区分 CPU 密集型和 I/O 密集型的线程模型差异
  2. 你知不知道这个默认值的历史背景和设计思路
  3. 你在生产环境会不会动手调,还是无脑用默认值
  4. 你能不能从线程调度、内存、下游依赖等多个维度综合分析

答不出后三点,基本就是纯背题选手。

先说结论:N+1 为什么不行

N+1(N = CPU 核心数)是给 CPU 密集型任务用的。核心逻辑很简单:CPU 核心就那么多,线程再多也只是在排队等 CPU 时间片,还得搭上上下文切换的开销。所以线程数 ≈ 核心数 + 1,多出来的一个做监控/日志之类的事。

但 Tomcat 不是这回事。

Tomcat 处理的是 HTTP 请求,本质是 I/O 密集型——一个请求的生命周期里,绝大部分时间线程是在等:等网络数据包、等数据库查询、等 RPC 返回、等文件读写。CPU 在这些接口等待期间是完全空闲的。

如果你只给一个 8 核的机器配 9 个线程,那同时最多只能处理 9 个请求,第 10 个请求就得排队——哪怕 CPU 利用率可能连 10% 都不到。这是典型的“线程在等 I/O,CPU 在睡觉”的场景。

所以 Tomcat 需要远多于 CPU 核心数的线程,让一部分线程等 I/O 的时候,CPU 去执行别的就绪线程。

200 这个数怎么来的

别神化它,200 就是个经验值。

背景是这样的:Tomcat 早期(Tomcat 4/5 时代)默认用的是 BIO(阻塞式 I/O)连接器,每个连接独占一个线程。当时的服务器内存可能就 2-4GB,每个线程占约 1MB 栈空间,200 个线程就是 200MB——在那个年代这已经是一笔不小的内存开销。

再高就有风险了:

  • 线程太多 → 上下文切换开销飙升
  • 内存吃不下 → OutOfMemoryError: unable to create new native thread
  • 设置过低 → 并发能力不够

200 就是在这三者之间取的一个保守平衡点。 后来引入了 NIO 连接器,线程模型优化了,但 200 作为默认值一直保留下来——这是中间件设计的一个原则:默认配置保证能跑起来,而不是跑得最好。

生产环境怎么调:压测才是唯一正解

拿默认值 200 直接上线,这不叫稳健,叫懒。

正确思路:

1)先粗算。 用利特尔法则:线程数 = QPS × 平均响应时间(秒)。目标 QPS 1000,平均 RT 0.1s,理论上需要 100 个线程。或者用 CPU核心数 × (1 + I/O等待时间/CPU计算时间) 估算一个起点。

2)再压测。 用 JMeter 或类似工具,从 50 开始,逐步调到 100、200、400、800,观察四个指标:

指标 看什么
QPS 是否随线程数增加而上升,达到某个点后不再涨
P95/P99 响应时间 是否开始飙升
CPU 使用率 是否达到 70-80% 的理想区间
内存和系统 Load 是否出现异常

3)找拐点。 当加线程时 QPS 不涨反降、RT 飙升的那个点,就是性能拐点。取拐点前一档的值,这才是你的最优 maxThreads。

三个常见误区,踩一个少一个

误区一:线程数越多越好。 超过拐点后,锁竞争加剧、上下文切换频繁、内存拉满,性能反而断崖式下降。越多越快是幻觉。

误区二:直接套公式。 任何公式都只能给起点。实际业务的复杂度、下游依赖(数据库连接池大小、缓存命中率、RPC 超时)都会影响最终结果。公式只是 starting point,压测才是 answer。

误区三:只调 Tomcat,不看全局。 maxThreads 调到 500,但数据库连接池只有 20?那 480 个线程全在等数据库连接。必须和数据库连接池 maxTotal、JVM 堆内存、操作系统 ulimit 等参数协同考虑。性能调优永远是系统性的,不是单点的。

文章目录
  1. 1. 这道题真正在筛什么人
  2. 2. 先说结论:N+1 为什么不行
  3. 3. 200 这个数怎么来的
  4. 4. 生产环境怎么调:压测才是唯一正解
  5. 5. 三个常见误区,踩一个少一个