JDK 6 对 synchronized 优化后引入偏向锁,分析其在无竞争环境中的性能提升原因?
- 内容介绍
- 相关推荐
本文共计681个文字,预计阅读时间需要3分钟。
它解决的根本问题不是并发控制,而是消除无竞争时的+CAS操作。在JDK 6之前,只有1个线程反覆调用synchronized方法,每次进入都要执行一次CAS去更新对象的Mark Word——看似轻量,但原子指令仍涉及内存屏障、缓存行刷新等隐含的隐秘性。偏向锁简化为一次内存读(比较线程ID),直接进入临界区,零同步原语。
触发偏向锁的三个硬性前提缺一不可
- JVM 启动参数未禁用:默认开启(
-XX:+UseBiasedLocking),但 JDK 15+ 默认关闭,需显式加该参数 - 对象未计算过
hashCode:一旦调用Object.hashCode()或System.identityHashCode(),对象头必须腾出空间存哈希值,会强制撤销偏向状态 - 未过 JVM 启动延迟期:默认前 4 秒内新建对象不启用偏向(
-XX:BiasedLockingStartupDelay=0可关掉)
性能收益只在特定模式下明显,且容易被误判
典型高收益场景:synchronized 修饰的 getter/setter、递归调用的同步方法、单线程循环中频繁加锁的计数器。实测显示,在纯单线程、百万次调用下,偏向锁比轻量级锁快 10%–20%,主要省在避免了 100 万次 CAS。
但以下情况反而拖慢:
- 多个线程交替访问同一对象:每次新线程来都会触发偏向撤销(revoke),代价远高于直接走轻量级锁
- 对象被大量用于
HashMap的 key:隐式调用hashCode,立刻退化为无锁状态 - 使用了
wait()/notify():JVM 会直接升级为重量级锁,偏向锁失效
查证偏向锁是否生效,不能只看代码写法
运行时状态得靠工具确认,比如用 jmap -histo:live 配合 -XX:+PrintSafepointStatistics 观察锁撤销次数,或用 JOL(Java Object Layout)打印对象头:new Object() 在偏向启用后,Mark Word 应含非零 thread_id 字段;若看到 lock=01 & biased_lock=0,说明已被禁用或撤销。
真正容易被忽略的是:偏向锁的收益高度依赖“访问模式稳定性”。一旦业务逻辑引入任意一个 hashCode 调用或跨线程共享,整个优化链就断了——它不像轻量级锁那样有自适应兜底,而是直接降级,且不可逆。
本文共计681个文字,预计阅读时间需要3分钟。
它解决的根本问题不是并发控制,而是消除无竞争时的+CAS操作。在JDK 6之前,只有1个线程反覆调用synchronized方法,每次进入都要执行一次CAS去更新对象的Mark Word——看似轻量,但原子指令仍涉及内存屏障、缓存行刷新等隐含的隐秘性。偏向锁简化为一次内存读(比较线程ID),直接进入临界区,零同步原语。
触发偏向锁的三个硬性前提缺一不可
- JVM 启动参数未禁用:默认开启(
-XX:+UseBiasedLocking),但 JDK 15+ 默认关闭,需显式加该参数 - 对象未计算过
hashCode:一旦调用Object.hashCode()或System.identityHashCode(),对象头必须腾出空间存哈希值,会强制撤销偏向状态 - 未过 JVM 启动延迟期:默认前 4 秒内新建对象不启用偏向(
-XX:BiasedLockingStartupDelay=0可关掉)
性能收益只在特定模式下明显,且容易被误判
典型高收益场景:synchronized 修饰的 getter/setter、递归调用的同步方法、单线程循环中频繁加锁的计数器。实测显示,在纯单线程、百万次调用下,偏向锁比轻量级锁快 10%–20%,主要省在避免了 100 万次 CAS。
但以下情况反而拖慢:
- 多个线程交替访问同一对象:每次新线程来都会触发偏向撤销(revoke),代价远高于直接走轻量级锁
- 对象被大量用于
HashMap的 key:隐式调用hashCode,立刻退化为无锁状态 - 使用了
wait()/notify():JVM 会直接升级为重量级锁,偏向锁失效
查证偏向锁是否生效,不能只看代码写法
运行时状态得靠工具确认,比如用 jmap -histo:live 配合 -XX:+PrintSafepointStatistics 观察锁撤销次数,或用 JOL(Java Object Layout)打印对象头:new Object() 在偏向启用后,Mark Word 应含非零 thread_id 字段;若看到 lock=01 & biased_lock=0,说明已被禁用或撤销。
真正容易被忽略的是:偏向锁的收益高度依赖“访问模式稳定性”。一旦业务逻辑引入任意一个 hashCode 调用或跨线程共享,整个优化链就断了——它不像轻量级锁那样有自适应兜底,而是直接降级,且不可逆。

