jvm自旋锁默认启用且自适应,-xx:+usespinning在jdk 6u23后废弃,实际由spinthreshold和spinlimit等内部参数动态调控,仅对短时轻竞争有效。

自旋锁在JVM里到底开没开?看-XX:+UseSpinning和JDK版本
OpenJDK 6u23之后,-XX:+UseSpinning已被废弃,JVM默认启用自适应自旋,不再需要手动开关。你配了也没用,JVM会直接忽略——这不是bug,是设计变更。
常见错误现象:java -XX:+UseSpinning -version启动时无报错也无提示,让人误以为生效;实际日志里查不到相关开关痕迹。
- JDK 6u23–JDK 7:自旋逻辑存在但硬编码开启,
-XX:+UseSpinning形同虚设 - JDK 8+:彻底移除该参数,自旋完全由
BiasedLocking和ObjectSynchronizer内部策略控制 - 真正影响自旋行为的是锁竞争历史,不是开关参数——比如同一对象被反复抢锁,JVM会悄悄延长下次自旋时间
自适应自旋怎么“适应”?看SpinThreshhold和SpinLimit
自适应不是AI预测,而是基于前一次获取锁是否成功的统计反馈:成功就加时长,失败就减时长,有上下限约束。
关键参数(仅HotSpot源码/调试版可见,生产环境通常不暴露):
-
SpinThreshhold:决定是否值得自旋的“竞争热度”阈值,比如连续5次抢锁失败后,下次就跳过自旋直接挂起 -
SpinLimit:单次自旋最大循环次数,默认30–100次(取决于CPU核数和锁粒度),超过立刻阻塞 - 注意:
SpinLimit不是固定值,JVM会在每次锁释放时根据等待线程数、上次自旋耗时动态调整
示例场景:一个ConcurrentHashMap的桶上频繁CAS失败,JVM发现连续3次自旋都空转超时,下一次就会直接走park(),避免浪费CPU。
为什么你的代码没看到自旋效果?检查锁对象生命周期和竞争模式
自旋只对“短时轻竞争”有效——即持有锁时间远小于线程调度周期(通常
- 锁对象复用率低(如每次new新对象):没有竞争历史,自适应机制无法积累数据,等同于无自旋
- 临界区含I/O或sleep(哪怕
Thread.sleep(1)):持有时间超标,JVM强制禁用后续自旋 - 高并发争抢同一把锁(>4线程):自旋成功率骤降,JVM主动降低
SpinLimit甚至归零 - 注意:
synchronized块内调用wait()会彻底破坏自旋前提——因为已进入ObjectMonitor.waitSet,不再是自旋适用场景
想验证自旋是否发生?别看jstack,盯紧Unsafe.park调用频次和GC日志里的ThreadState
jstack输出全是BLOCKED或WAITING,根本看不出有没有自旋过。真实线索藏在更底层:
- 用
-XX:+PrintGCDetails配合-XX:+UnlockDiagnosticVMOptions -XX:+LogVMOutput,观察GC线程停顿前是否有密集的os::is_interrupted轮询(自旋特征) - 用
perf record -e cycles,instructions,task-clock -g -- java MyApp看热点是否集中在ObjectSynchronizer::fast_enter内的循环段 - 最直接办法:在
src/hotspot/share/runtime/synchronizer.cpp里给spin_next打点编译调试版——但生产环境别这么干
容易被忽略的一点:自旋只发生在偏向锁撤销后、轻量级锁膨胀前的那几纳秒窗口。这个阶段既不进monitor enter慢路径,也不触发park,常规监控工具天然“看不见”。










