OpenJDK24下Tomcat启用虚拟线程时载体线程固定问题咨询
环境信息
- jdk:openjdk24
- tomcat:10.1.40
- 虚拟线程启用:true
- 问题:载体线程固定(carrier thread pinned)
- 诊断文件链接:
- 详细jstack信息
- 详细jcmd虚拟线程信息
"ForkJoinPool-1-worker-1" #50 [983] daemon prio=5 os_prio=0 cpu=179.14ms elapsed=63.41s tid=0x00007f186800aee0 [0x00007f1913211000] 承载虚拟线程 #157 at jdk.internal.vm.Continuation.run(java.base@24/Continuation.java:254) at java.lang.VirtualThread.runContinuation(java.base@24/VirtualThread.java:303) at java.lang.VirtualThread$$Lambda/0x00007f18d33c2598.run(java.base@24/Unknown Source) at java.util.concurrent.ForkJoinTask$AdaptedRunnableAction.exec(java.base@24/ForkJoinTask.java:1586) at java.util.concurrent.ForkJoinTask.doExec(java.base@24/ForkJoinTask.java:507) at java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(java.base@24/ForkJoinPool.java:1394) at java.util.concurrent.ForkJoinPool.runWorker(java.base@24/ForkJoinPool.java:1970) at java.util.concurrent.ForkJoinWorkerThread.run(java.base@24/ForkJoinWorkerThread.java:187) 挂载的虚拟线程 #157 at org.apache.tomcat.util.res.StringManager.getManager(StringManager.java:219) - 等待锁定 <0x00000006ce8a75c0>(对应org.apache.tomcat.util.res.StringManager类的Class对象) at org.apache.tomcat.util.res.StringManager.getManager(StringManager.java:204) at org.apache.tomcat.util.res.StringManager.getManager(StringManager.java:191) at org.apache.catalina.connector.CoyoteOutputStream.<clinit>(CoyoteOutputStream.java:36) at org.apache.catalina.connector.Response.getOutputStream(Response.java:509) at org.apache.catalina.connector.ResponseFacade.getOutputStream(ResponseFacade.java:179) at org.springframework.web.context.request.async.StandardServletAsyncWebRequest$LifecycleHttpServletResponse.getOutputStream(StandardServletAsyncWebRequest.java:286) at org.springframework.http.server.ServletServerHttpResponse.getBody(ServletServerHttpResponse.java:98) at com.huya.extention.wup.WupHttpMessageConverter.writeInternal(WupHttpMessageConverter.java:176) at com.huya.extention.wup.WupHttpMessageConverter.writeInternal(WupHttpMessageConverter.java:34) at ... at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1740) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) at java.lang.Thread.runWith(java.base@24/Thread.java:1460) at java.lang.VirtualThread.run(java.base@24/VirtualThread.java:466) at java.lang.VirtualThread$VThreadContinuation$1.run(java.base@24/VirtualThread.java:258) at jdk.internal.vm.Continuation.enter0(java.base@24/Continuation.java:325) at jdk.internal.vm.Continuation.enter(java.base@24/Continuation.java:316)
其他线程jstack信息
"ThreadPoolTaskExecutor-4" #195 [1063] prio=5 os_prio=0 cpu=45.27ms elapsed=48.81s tid=0x00007f178c2334a0 nid=1063 等待获取监视器锁 [0x00007f18d0ebc000] java.lang.Thread.State: BLOCKED (等待监视器锁) at org.springframework.boot.loader.net.protocol.jar.UrlJarFiles$Cache.get(UrlJarFiles.java:158) - 等待锁定 <0x00000006ccc4f6f8>(对应org.springframework.boot.loader.net.protocol.jar.UrlJarFiles$Cache对象) at org.springframework.boot.loader.net.protocol.jar.UrlJarFiles.getCached(UrlJarFiles.java:81) at ... at java.lang.ClassLoader.loadClass(java.base@24/ClassLoader.java:557) - 已锁定 <0x00000006d96cdc38>(对应一个java.lang.Object对象) at org.springframework.boot.loader.net.protocol.jar.JarUrlClassLoader.loadClass(JarUrlJarClassLoader.java:107) at org.springframework.boot.loader.launch.LaunchedClassLoader.loadClass(LaunchedClassLoader.java:91) at java.lang.ClassLoader.loadClass(java.base@24/ClassLoader.java:490) at ... at org.apache.commons.beanutils.PropertyUtils.getProperty(PropertyUtils.java:464) at com.huya.passport.handler.MetricsLogReportDispatcher.handle(MetricsLogReportDispatcher.java:227) at com.huya.passport.kafka.ReportService.send(ReportService.java:87) at ... at java.lang.Thread.run(java.base@24/Thread.java:1447)
相关截图
相似问题参考
- JDK-8369238
- OpenJDK修复PR
- Quarkus相关问题
咨询问题
- 为什么OpenJDK24环境下,遇到static synchronized仍会出现载体线程固定?
- 该问题如何解决?是否需要修复Tomcat库?
已发现信息
- JDK26已修复该问题(对应JDK-8369238)
- JDK25临时解决方法:在程序启动线程中调用静态类方法(如HexUtils.getHex(5))
问题解答
1. OpenJDK24出现该问题的原因
OpenJDK 24中,虚拟线程执行static synchronized方法或代码块时,会锁定对应的Class对象。但该版本的虚拟线程实现未针对Class对象的内置锁(监视器)做非固定调度优化——虚拟线程持有这类锁时无法被挂起切换到其他载体线程,必须持续占用当前载体线程直到锁释放,最终导致载体线程被固定。这是JDK层面的实现缺陷,对应问题编号JDK-8369238,直到JDK26才完成修复。
从提供的线程栈可见,虚拟线程#157正等待锁定org.apache.tomcat.util.res.StringManager的Class对象,此时载体线程ForkJoinPool-1-worker-1被固定,无法处理其他虚拟线程任务。
2. 解决方法及是否需要修复Tomcat
不需要修改Tomcat库,可根据当前使用的JDK版本选择对应方案:
- 长期方案:升级到OpenJDK26,该版本已彻底修复Class对象锁导致虚拟线程固定的问题。
- JDK25临时方案:在程序启动阶段(如main线程初始化流程中),提前调用涉及
static synchronized的类的静态方法,触发类初始化并让启动线程完成锁相关逻辑,避免后续虚拟线程处理请求时因类初始化触发锁竞争导致载体线程固定。 - JDK24临时方案:
- 将
static synchronized替换为显式锁(如ReentrantLock),虚拟线程对显式锁的支持更友好,不会触发载体线程固定; - 提前初始化涉及
static synchronized的类,避免虚拟线程在请求处理过程中触发类初始化锁竞争。
- 将
内容的提问来源于stack exchange,提问作者walker_fish
相关产品推荐
相关产品推荐




