题目:为什么虚拟线程不能用 synchronized?
难度:medium
标签:java/concurrency-practice, java/concurrency, general/knowledge, difficulty/medium
评分:⭐⭐⭐☆☆(3/5)
虚拟线程不用 synchronized 的核心原因是 synchronized 是操作系统层面的锁,会直接将虚拟线程对应的系统线程锁住,这样最后会导致虚拟线程退化成常规线程,和系统线程一对一,会影响虚拟线程使用,可以用 java 的 ReentrantLock 来做锁操作。
优点:
不足:
-Djdk.tracePinnedThreads / JFR jdk.VirtualThreadPinned)最后更新:2026-10-26 · 基于第 1 次作答
结论:不是"synchronized 不能用",而是"别在 synchronized 块里阻塞"——阻塞时 carrier thread 被 pin。 关键词:pinning · Monitor owner 与 OS 线程绑定 · ReentrantLock uncontended 不 pin 边界:ReentrantLock 竞争时也 pin;彻底避免靠 CAS/无锁;JNI 也会 pin
核心考点定位:能否准确理解 pinning 的机制(不是"退化为 OS 线程",而是 carrier thread 被固定),能否给出边界清晰的替代方案。
机制链(3 环):
synchronized 持锁期间阻塞 → Monitor owner 关联在 OS 线程上,栈帧无法安全挂起 → carrier thread 陪着等待 → 这就是 pinning证据锚点(去重 ≥10):
-Djdk.tracePinnedThreads=full:JVM 参数,打印 pin 调用栈jdk.VirtualThreadPinned 事件:监控 pinning-Djdk.virtualThreadScheduler.mountPinningPolicy 尝试缓解Thread.onSpinWait():某些场景也会 pin对比与权衡:
| 锁类型 | uncontended 阻塞 | 竞争时阻塞 | 其他阻塞 | pin 程度 |
|---|---|---|---|---|
synchronized | pin | pin | pin | 高 |
ReentrantLock | 不 pin | pin | 不 pin | 中 |
tryLock(timeout) | 不 pin | 不 pin(超时放弃) | 不 pin | 低 |
| CAS / AtomicReference | 不 pin | 不 pin | 不 pin | 无 |
| JNI native | — | — | pin | 高 |
常见误区:
适用边界:
synchronized 块内纯计算(不阻塞);用 ReentrantLock 缓解;高竞争用 CAS / 无锁synchronized 块内 IO 阻塞、sleep、join;JNI 密集场景ReentrantLock uncontended 无 pin;tryLock 超时放弃;CAS / AtomicReference 彻底无锁tryLock 非阻塞模式(超时直接放弃,不 pin)-Djdk.virtualThreadScheduler.mountPinningPolicy 尝试缓解;JDK 22 主要是改进调度器jdk.VirtualThreadPinned 事件;carrier thread 利用率低说明有 pin;-Djdk.tracePinnedThreads 打印栈【结论】 不是"synchronized 不能用",而是"别在 synchronized 块里阻塞"——阻塞时 carrier thread 被 pin 无法卸载。
【三点】
第一,为什么 pin:虚拟线程靠 Continuation 挂起——阻塞时把栈帧保存到堆内存,释放 carrier thread 干别的。但 synchronized 块持锁期间如果阻塞(IO、sleep、join),JVM 不能挂起这个栈帧,因为锁的 Monitor owner 关联在 OS 线程上,一旦切换走再回来可能破坏锁的一致性。所以 carrier thread 只能陪着这个虚拟线程一起等——这就是 pinning。
第二,准确的说法:不是"synchronized 不能用",而是"别在 synchronized 块里阻塞"。如果 synchronized 块内是纯计算、快速方法,其实不 pin,JDK 21 官方立场也是"尽量用,只是别在其中阻塞"。
第三,替代方案:首选 ReentrantLock——uncontained 时完全不 pin,只有真正竞争(park)时才可能 pin,pin 概率远低于 synchronized。更彻底用 CAS + AtomicReference 无锁化,或 tryLock 非阻塞模式(超时直接放弃,不 pin)。注意:JNI / native 方法也会 pin,与锁无关。排查靠 -Djdk.tracePinnedThreads=full 或 JFR 的 jdk.VirtualThreadPinned 事件。
【边界】 两点提醒:一是 ReentrantLock 也不是零 pin,高竞争下还是会有;二是"完全无锁"要付出正确性代价,生产要根据业务权衡选择——语义正确性永远优先于 pinning 规避。
| 日期 | 评分 | 备注 |
|---|---|---|
| 2026-10-26 | ⭐⭐⭐☆☆ | 首次作答 |