maxthreads 合理值需平衡i/o等待与cpu争抢,非越大越好;建议从200起步,依jmx监控busy线程峰值及延迟调整,并注意堆外内存与gc影响。

maxThreads 设为多少才算合理
不是越大越好,也不是照着 CPU 核数硬配。线程数本质是在「等待 I/O」和「争抢 CPU」之间找平衡点。如果应用大量调用数据库或远程 HTTP 接口,maxThreads 设太高反而会因线程上下文切换、内存占用激增导致吞吐下降。
实操建议:
- 先观察生产环境的
http-nio-8080线程池实际使用峰值(可通过 JMX 查currentThreadCount和currentThreadsBusy) - 若平均
currentThreadsBusy长期 > 70%maxThreads,且响应延迟上升,再考虑上调 - 新部署服务可从
200起步;高并发读写少的后台接口(如定时任务触发端点),50–100往往更稳 - 注意 JVM 堆外内存:每个线程默认栈大小约 1MB(由
-Xss控制),设成 1000 线程 ≈ 额外 1GB 堆外内存开销
修改 server.xml 后不生效的常见原因
改完 server.xml 不等于立刻生效——Tomcat 启动时只读一次配置,且部分参数在连接器初始化后锁定。
常见错误现象:maxThreads 明明改了,但 JMX 里还是旧值;或重启后日志里报 Invalid value for maxThreads。
实操建议:
- 确认改的是正在运行实例的
$CATALINA_HOME/conf/server.xml,不是模板或备份文件 - 检查
<connector></connector>标签是否被注释,或存在多个<connector></connector>(比如同时启用了 HTTP 和 AJP),需逐个核对 - 确保没有在
context.xml或应用内通过EmbeddedServletContainerCustomizer等方式覆盖了线程配置 - 重启必须是完整 stop → start,仅 reload webapp 不会重载
server.xml
maxThreads 和 acceptCount 的配合逻辑
acceptCount 不是“排队线程数”,而是操作系统 TCP 连接队列长度。当所有线程都在忙,新连接会先进入这个队列;队列满后,新连接直接被 OS 拒绝(表现为 connection refused)。
二者关系直接影响用户体验:
- 若
maxThreads=200,acceptCount=100,瞬时流量 400,前 200 进线程处理,中间 100 排队,最后 100 直接失败 - 盲目调大
acceptCount(比如设到 1000)可能让客户端等太久(超时断开),而服务端还在傻等 - 更稳妥的做法是:压测中观察
connection refused出现时机,结合netstat -an | grep :8080 | grep SYN_RECV判断队列是否打满
Spring Boot 内嵌 Tomcat 下怎么调 maxThreads
Spring Boot 默认用内嵌 Tomcat,server.xml 根本不存在——直接改它没用。
正确路径是通过配置项驱动:
- 在
application.properties中加:server.tomcat.threads.max=300 - 对应 YAML 写法:
server: tomcat: threads: max: 300 - 注意命名空间:不是
spring.servlet.tomcat...,也不是server.http...,错一个词就静默失效 - 如果用了
WebServerFactoryCustomizer<tomcatservletwebserverfactory></tomcatservletwebserverfactory>编程式定制,务必在getTomcatConnectorCustomizers()里设置,别在 connector 构造后才 set
线程数调得过高最隐蔽的问题不是崩溃,而是 GC 频率突增、Full GC 后线程卡住几秒——这时候看线程名还是 http-nio-8080-exec-xxx,但堆栈停在 java.lang.Thread.sleep 或 Unsafe.park,其实是 GC 正在回收堆外内存。真要调,一定带着 GC 日志一起压测。










