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

Spring应用大量TIMED_WAITING线程问题排查求助

Spring Boot应用压测后TIMED_WAITING线程激增及二次压测超时排查方案

现象梳理

  • 应用启动初始线程状态:blocked:0,new:0,runnable:17,terminated:0,time-waiting:21,waiting:12
  • 首次执行压测命令:ab -s 300 -n 100000 -c 50 "http://my-address/v1/my-collection",压测后线程状态变为:blocked:0,new:0,runnable:21,terminated:0,time-waiting:73,waiting:12
  • 二次执行相同压测时出现超时,仅完成45339请求
  • 线程dump显示http-nio线程及ThreadPoolTaskScheduler线程处于TIMED_WAITING状态

核心排查方向及步骤

1. 拆解TIMED_WAITING线程的等待原因

TIMED_WAITING本身不一定是异常,但数量激增且伴随压测超时,需结合线程栈具体分析:

  • http-nio线程:这类线程属于Tomcat NIO线程池,通常因等待新请求或连接进入TIMED_WAITING。查看线程栈中是否存在如下调用链:

    java.lang.Thread.State: TIMED_WAITING (parking)
    at jdk.internal.misc.Unsafe.park(Native Method)
    at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252)
    at org.apache.tomcat.util.net.NioEndpoint$Poller.run(NioEndpoint.java:719)
    

    若大量线程停留在NioEndpoint$Poller.run,说明Tomcat线程池已处理完当前请求,但未被回收(默认keepAliveTime为60秒);若线程停留在业务方法调用栈,则是请求处理慢导致线程被占用。

  • ThreadPoolTaskScheduler线程:这类线程多对应定时任务或延迟任务,正常状态是等待下一次调度,典型栈信息:

    java.lang.Thread.State: TIMED_WAITING (parking)
    at jdk.internal.misc.Unsafe.park(Native Method)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:1672)
    at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1182)
    

    若此类线程数量异常,需检查是否存在大量重复提交的调度任务,或任务执行时间超过调度间隔导致线程池扩容。

2. 验证Tomcat线程池配置合理性

Spring Boot 2.7.x默认Tomcat线程池参数为:maxThreads=200、acceptCount=100、minSpareThreads=10、connectionTimeout=20000。针对压测场景需重点关注:

  • 通过Actuator指标tomcat.threads.busy、tomcat.threads.current、tomcat.requests.active确认线程池是否饱和
  • 若请求处理耗时较长(如>1s),50并发下每秒仅能处理50个请求,100000请求需2000秒,远超ab的-s 300超时时间,会导致压测中断
  • 调整Tomcat配置优化并发能力:
    server.tomcat.threads.max=300
    server.tomcat.threads.min-spare=50
    server.tomcat.accept-count=200
    server.tomcat.connection-timeout=60000
    

3. 排查业务链路瓶颈

  • 用Arthas的trace命令跟踪请求入口方法,定位耗时最长的环节:
    arthas trace com.your.package.controller.MyController myMethod -n 10
    
  • 重点检查数据库查询、远程调用、IO操作等环节,是否存在慢SQL、连接池不足(如HikariCP默认maximum-pool-size=10,50并发下会导致线程等待连接)

4. 优化ZGC配置

当前JVM配置Xms248MB、Xmx3968MB,ZGC建议Xms与Xmx设为相同值,避免内存频繁扩容引发的GC停顿:

-Xms3968M -Xmx3968M -XX:+UseZGC -XX:+ZGenerational -XX:+PrintGC

通过GC日志确认是否存在频繁GC或停顿过长影响请求处理。

5. 规范ThreadPoolTaskScheduler使用

  • 若应用中使用@Scheduled或手动创建调度器,需确保:
    • 任务执行时间小于调度间隔,避免线程被长期占用
    • 配置合理的线程池大小,避免因任务排队导致线程池扩容:
      @Configuration
      public class SchedulerConfig {
          @Bean
          public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
              ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
              scheduler.setPoolSize(5);
              scheduler.setThreadNamePrefix("scheduler-");
              scheduler.setWaitForTasksToCompleteOnShutdown(true);
              return scheduler;
          }
      }
      

可能的根因总结

  1. 业务请求处理链路存在慢操作(如慢SQL、远程调用超时),导致线程被长期占用,后续请求排队超时
  2. Tomcat线程池或数据库连接池配置不足,无法支撑50并发的请求压力
  3. ZGC内存初始值过小,引发频繁内存扩容与GC停顿,影响请求处理效率
  4. ThreadPoolTaskScheduler因大量调度任务或任务执行超时,导致线程池扩容,多余线程进入TIMED_WAITING状态

内容的提问来源于stack exchange,提问作者rios0rios0

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 21:51:14