JDK 15后默认禁用偏向锁,核心原因是其撤销代价(需STW、遍历栈帧、CAS更新)远超收益,现代多线程高并发场景下轻量级锁+自旋等优化已更高效稳定。

偏向锁在 JDK 15 之后默认禁用,核心原因不是它“失效了”,而是它的收益已无法覆盖现代高并发场景下的撤销代价。
偏向锁的设计初衷与现实脱节
它原本为单线程反复获取同一把锁的场景而生——比如单例初始化、配置加载等长生命周期对象的独占访问。此时省掉每次 CAS,确实能降开销。但如今微服务、RPC、连接池、缓存 key 锁等典型场景中,一个锁常被多个线程轮番争用,刚“偏”上就被迫撤销。锁还没热身,就进入清理流程。
一次撤销 = 一次隐式 STW
当其他线程尝试获取已被偏向的锁时,JVM 必须执行撤销(revocation):
- 等待所有线程到达全局安全点(Safepoint)
- 挂起持有偏向锁的线程
- 遍历其栈帧,检查是否仍在同步块内
- 根据状态决定是直接清除偏向,还是升级为轻量级锁
- 最后 CAS 更新对象头 Mark Word
这一整套操作会引发毫秒级停顿。在短生命周期对象高频加锁的 Web 容器里,撤销耗时可能占同步总开销的 20% 以上。
轻量级锁已足够高效
现代 CPU 的 CAS 指令延迟大幅下降,加上 JVM 自适应自旋、锁消除、锁粗化等成熟优化,使得无竞争或低竞争下轻量级锁的性能接近甚至优于偏向锁:
- 轻量级锁不依赖线程 ID 记录,无需维护 epoch 或批量重偏向逻辑
- 没有撤销路径,锁升级路径更简洁:无锁 → 轻量级锁 → 重量级锁
- SPECjbb2015 等基准测试显示,禁用偏向锁后 99% 延迟降低 5%–10%,吞吐量无损
禁用后如何验证是否生效
不能只看启动参数,要确认运行时行为:
- 加参数:-XX:+PrintBiasedLockingStatistics -XX:+UnlockDiagnosticVMOptions
- 启动后搜索日志:biased locking is disabled 或 Total skipped revocations: 0
- 避免误判:jstat 和 JMX 中的锁统计不反映撤销频率,不可靠
对于 JDK 8–14 的老服务,务必手动添加 -XX:-UseBiasedLocking,并确保参数写在 -jar 左侧,否则可能被忽略。










