添加-Djdk.tracePinnedThreads=full致Spring Boot虚拟线程测试挂起求助
虚拟线程挂起问题分析(带
-Djdk.tracePinnedThreads=full参数时) 问题现象
在Spring Boot 3.2.0 + Java 21环境下启用虚拟线程后,添加-Djdk.tracePinnedThreads=full参数时测试会挂起;移除该参数则运行正常。测试逻辑为启动3个虚拟线程调用同步方法testForPin,该方法持有synchronized锁并执行Thread.sleep(1000)。通过jhsdb排查发现无死锁,仅一个虚拟线程持锁处于sleep状态,另外两个虚拟线程阻塞等待锁。
环境配置
- pom.xml:Spring Boot 3.2.0,指定Java版本21
- application.properties:已启用虚拟线程,配置应用保持存活
- 测试代码:3个虚拟线程调用带
synchronized修饰的testForPin方法,方法内持有锁并执行Thread.sleep(1000)
原因分析
-Djdk.tracePinnedThreads=full是JVM用于追踪虚拟线程**固定(Pinned)**状态的参数:当虚拟线程持有synchronized这类监视器锁时,会被固定到载体线程(Carrier Thread)上,无法被JVM调度切换。开启full级别追踪时,JVM会在虚拟线程被固定的时机执行大量栈遍历、日志输出操作,这会占用载体线程的大量资源,导致其执行被阻塞,进而让等待锁的其他虚拟线程无法被正常调度,最终表现为测试挂起。
这个问题本质是JVM追踪参数的行为与synchronized锁导致的虚拟线程固定特性冲突,和Spring的核心配置无关,但可以通过调整锁的使用方式来规避。
解决方案与优化建议
- 替换锁类型:将
synchronized锁替换为java.util.concurrent包中的显式锁(比如ReentrantLock),虚拟线程持有这类锁时不会被固定到载体线程,JVM可以正常调度其他虚拟线程,即使开启-Djdk.tracePinnedThreads=full也不会出现挂起。 - 降低追踪级别:如果必须保留
synchronized锁,可将参数改为-Djdk.tracePinnedThreads=short,减少日志输出的开销,避免载体线程被阻塞。 - Spring配置确认:确保已正确启用虚拟线程(配置
spring.threads.virtual.enabled=true),Spring本身无需额外特殊配置来解决该问题,核心优化点在锁的使用和JVM参数调整。
内容的提问来源于stack exchange,提问作者Craig Archer
相关产品推荐
相关产品推荐

