偏向锁通过首次CAS写入线程ID、后续仅比对ID实现零开销重入,专为单线程反复进入同一同步块设计;它要求对象未调用hashCode、JVM未禁用且过延迟期,否则退化为轻量级锁,JDK15起默认关闭、JDK17+彻底移除。

synchronized 利用偏向锁优化无竞争场景,核心是把“每次加锁都要 CAS”变成“只比对一次线程 ID”。它不解决多线程安全问题,而是专为单线程反复进入同一同步块而设计——只要没别的线程来抢,后续所有加锁操作都变成一次内存读取,几乎零开销。
偏向锁怎么生效的
对象刚创建时处于无锁状态。当第一个线程执行 synchronized 时,JVM 检查满足三个前提后,就将对象头 Mark Word 标记为偏向锁,并写入该线程 ID:
- JVM 参数未禁用偏向锁(默认开启,-XX:+UseBiasedLocking)
- 对象尚未调用过 hashCode() 或 System.identityHashCode()(否则 Mark Word 需存哈希值,无法容纳线程 ID)
- 对象创建时间在 JVM 启动后延迟期之后(默认前 4 秒内新建对象不启用,可用 -XX:BiasedLockingStartupDelay=0 关闭)
为什么能省性能
没有偏向锁时,哪怕只有 1 个线程反复调用 synchronized 方法,每次都要走轻量级锁路径:在栈上建 Lock Record、CAS 更新 Mark Word。CAS 虽轻量,但涉及内存屏障、缓存行刷新等隐性成本。
启用偏向锁后:
立即学习“Java免费学习笔记(深入)”;
- 首次获取锁:一次 CAS(记录线程 ID)
- 后续同一线程重入:仅读取 Mark Word 中的 thread_id 字段,比对是否为自己 → 匹配则直接进临界区
百万次调用下,可省掉百万次 CAS,实测快 10%–20%。
哪些代码容易受益
典型适合偏向锁的模式有:
- synchronized 修饰的 getter/setter(如 POJO 的字段访问)
- 单线程循环中频繁调用的同步方法(如计数器累加)
- 初始化阶段由主线程串行构建的对象图,期间大量同步操作
注意:这些场景必须真正“无跨线程共享”,一旦某个地方隐式调用了 hashCode(比如放进 HashMap 作 key),偏向状态立刻失效,退化为无锁或轻量级锁。
怎么确认它起了作用
不能光看代码写了 synchronized 就认为用了偏向锁。运行时状态需工具验证:
- 用 JOL(Java Object Layout) 打印对象头:
new Object()在启用偏向锁后,Mark Word 应含非零 thread_id;若 lock=01 & biased_lock=0,说明已被禁用或撤销 - 用 jmap -histo:live 配合 -XX:+PrintSafepointStatistics 观察偏向锁撤销次数,频繁撤销说明不适合用
偏向锁收益高度依赖访问模式稳定性——它不像轻量级锁那样有自适应兜底,一旦被撤销,不可逆降级,整个优化链就断了。


















