You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tomcat不复用等待线程反而创建新线程问题排查求助

问题描述

将Jira 8.13部署在Apache Tomcat/8.5.60集群环境中,部分节点会随机出现不复用处于WAITING状态的空闲HTTP线程、反而创建新线程的异常情况。最终Tomcat达到maxThreads上限后停止接收新请求,但应用其他模块仍可正常运行。Tomcat及应用日志无异常输出,仅能通过重启Tomcat恢复。

相关线程dump(为等待任务的HTTP线程正常状态):

http-nio-8080-exec-2618 sun.misc.Unsafe.park(Native Method) java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215) java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078) java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:467) org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:90) org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:33) java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1073) java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) java.lang.Thread.run(Thread.java:748)

当前Connector配置:

<Connector port="8080" relaxedPathChars="[]|" relaxedQueryChars="[]|{}^&#x5c;&#x60;&quot;&lt;&gt;"
    maxThreads="200" minSpareThreads="25" enableLookups="false"
    maxHttpHeaderSize="8192" protocol="HTTP/1.1" useBodyEncodingForURI="true" redirectPort="8443"
    acceptCount="100" connectionTimeout="120000" disableUploadTimeout="true" bindOnInit="false"
    secure="true" scheme="https" proxyName="*" proxyPort="443" keyAlias="jira" keystoreFile="/* /jira.jks" keystorePass="*" keystoreType="JKS"/>
可能的原因
  1. Tomcat TaskQueue复用逻辑缺陷:Tomcat默认的TaskQueue继承自LinkedBlockingQueue,在高并发或特定任务场景下,线程池可能误判无空闲线程,进而创建新线程。Tomcat 8.5.x部分版本存在线程池调度的逻辑偏差,在任务队列状态变化时的判断不够严谨。
  2. ThreadLocal变量泄漏:Jira应用中若存在未清理的ThreadLocal绑定资源,会导致线程虽处于WAITING状态,但线程池无法正确识别其可复用性,被迫创建新线程。
  3. 连接状态与线程标记不匹配:当前connectionTimeout=120000(2分钟),若客户端异常断开连接,Tomcat可能未及时将对应线程标记为可复用,导致线程池误判。
  4. 集群负载不均衡:集群节点请求分布不均,部分节点长期高负载,触发线程池调度逻辑异常,无法正确识别空闲线程。
调试方法
  • 实时监控线程池指标:用JConsole、VisualVM等JMX工具监控Tomcat线程池的currentThreadsBusy、currentThreadCount、queueSize等指标,记录异常发生时的数值变化,对比空闲线程数与新线程触发条件。
  • 捕获异常时的全量dump:问题出现时立即执行jstack获取全量线程dump、jmap获取堆dump,分析是否存在ThreadLocal泄漏、线程实际状态及任务队列情况。
  • 调整Tomcat日志级别:修改conf/logging.properties,将org.apache.tomcat.util.threads的日志级别设为FINE,观察线程调度、任务分配的详细日志,定位复用失败的触发点。
  • 模拟场景复现:在测试环境模拟高并发、客户端异常断开、长连接等场景,验证问题是否与特定请求模式相关。
临时解决方案

你考虑的配置独立Executor并设置maxIdleTime是可行的,具体配置建议:

  1. 在server.xml中添加独立Executor:
<Executor name="tomcatThreadPool" namePrefix="http-nio-8080-exec-"
    maxThreads="200" minSpareThreads="25" maxIdleTime="60000"
    maxQueueSize="100"/>
  1. 修改Connector引用该Executor:
<Connector port="8080" relaxedPathChars="[]|" relaxedQueryChars="[]|{}^&#x5c;&#x60;&quot;&lt;&gt;"
    enableLookups="false" maxHttpHeaderSize="8192" protocol="HTTP/1.1"
    useBodyEncodingForURI="true" redirectPort="8443" acceptCount="100"
    connectionTimeout="120000" disableUploadTimeout="true" bindOnInit="false"
    secure="true" scheme="https" proxyName="*" proxyPort="443" keyAlias="jira"
    keystoreFile="/* /jira.jks" keystorePass="*" keystoreType="JKS"
    executor="tomcatThreadPool"/>

maxIdleTime=60000(1分钟)会让空闲线程超时后自动销毁,避免堆积无法复用的线程,同时独立Executor的调度逻辑可规避默认线程池的复用缺陷。

内容的提问来源于stack exchange,提问作者Кирилл Ленченков

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 20:27:48