【JUC】深入剖析 synchronized 锁升级机制:从偏向锁到重量级锁
目录
3. 偏向锁 vs 轻量级锁的 Lock Record 区别
在上一篇文章中,我们讲解了 synchronized 的基础使用和底层 Monitor 重量级锁原理,而 JDK 1.6 之后 synchronized 之所以性能大幅提升,核心原因就是引入了锁升级机制(偏向锁 → 轻量级锁 → 重量级锁)。本文将带你彻底搞懂这三种锁的实现原理、升级时机和核心区别。
一、锁升级的原因
1. JDK 1.6 之前的痛点
JDK 1.6 之前,synchronized 只有重量级锁一种形态,完全依赖操作系统的互斥量(Mutex)和 JVM 的 Monitor 实现。线程的阻塞 / 唤醒需要从用户态切换到内核态,每次切换开销约为几十纳秒到几微秒,而普通的方法调用仅需几纳秒 —— 这种 “杀鸡用牛刀” 的设计,让 synchronized 在低竞争场景下性能极差。
2. 优化的核心依据
JVM 团队通过大量实践观察到两个关键特征:
- 偏向性:大多数场景下,锁只会被一个线程重复获取(无竞争);
- 短暂性:即使有竞争,也只是多个线程短时间交替争抢,而非长时间占用。
基于这两个特征,JVM 设计了按需升级的锁策略:从开销最小的偏向锁开始,仅当竞争加剧时才升级为轻量级锁、最终到重量级锁,兼顾不同竞争场景的性能
二、核心基础:Mark Word 结构
锁升级的本质是修改对象头中 Mark Word(标记字段)的内容和标志位,理解 Mark Word 是掌握锁升级的前提。

- hashcode:25位的对象标识Hash码
- age:对象分代年龄占4位
- biased_lock:偏向锁标识,占1位 ,0表示没有开始偏向锁,1表示开启了偏向锁
- thread:持有偏向锁的线程ID,占23位
- epoch:偏向时间戳,占2位
- ptr_to_lock_record:轻量级锁状态下,指向栈中锁记录的指针,占30位
- ptr_to_heavyweight_monitor:重量级锁状态下,指向对象监视器Monitor的指针,占30位
以 64 位 JVM 为例,Mark Word 结构如下(核心字段):
| 锁状态 | 64 位 Mark Word 存储内容 | 标志位(最后 2 位) | 偏向锁标志(倒数 3 位) |
|---|---|---|---|
| 无锁 | 对象哈希码(25bit) + 分代年龄(4bit) + 未使用(1bit) + 无锁标志 | 01 | 0 |
| 偏向锁 | 偏向线程 ID(54bit) + epoch(2bit) + 分代年龄(4bit) + 偏向锁标志 | 01 | 1 |
| 轻量级锁 | 指向栈中锁记录(Lock Record)的指针(62bit) + 轻量级锁标志 | 00 | - |
| 重量级锁 | 指向 Monitor 对象的指针(62bit) + 重量级锁标志 | 10 | - |
| GC 标记 | 空 + GC 标记位 | 11 | - |
关键说明:
- 所有锁状态的切换,都是通过 CAS 操作修改 Mark Word 的内容和标志位实现;
- 偏向锁和无锁的标志位都是 01,通过 “偏向锁标志位” 区分;
- 轻量级 / 重量级锁的核心是 Mark Word 存储 “指针”(指向栈中锁记录或 Monitor)。
关键:所有锁状态的切换,本质都是修改 Mark Word 的标志位和存储内容,无需创建新对象,开销极小。
三、偏向锁:无竞争场景的最优解
1. 核心思想
偏向锁的目标是:在只有一个线程获取锁的场景下,消除锁的获取 / 释放开销。
“偏向” 就是指锁会偏向于第一个获取它的线程,只要后续没有其他线程竞争,该线程就永远不需要再进行加锁 / 解锁操作。
2. 实现原理
(1)首次获取偏向锁(对应你描述的核心流程)
1.创建 Lock Record:线程进入同步代码块时,JVM 会在当前线程的栈帧中创建一个 Lock Record(锁记录) 结构体,将其 obj 字段指向当前锁对象。

2.CAS 抢占偏向锁:
- 线程通过 CAS 指令,将自身线程 ID 存储到锁对象的 Mark Word 中;
- 同时将 Mark Word 的最后 3 位标志位设置为
101(偏向锁标识);- 若对象处于无锁状态(标志位 001),CAS 修改成功,该线程获得偏向锁。

3.关联 Lock Record 与锁对象:完成 Lock Record 和锁对象的绑定,线程开始执行同步代码。
(2)锁重入(核心优化:无 CAS 操作)
线程再次进入同步代码块(锁重入)时:
1.JVM 在栈帧中创建一个新的 Lock Record;

2.轻量级检查:仅判断锁对象 Mark Word 中的线程 ID 是否为当前线程 ID,无需执行 CAS 操作;
3.标记重入计数:将新创建的 Lock Record 的核心字段设为 null,以此作为 “重入计数器”—— 栈帧中有多少个 null 的 Lock Record,就代表锁重入了多少次;
相比轻量级锁 “每次重入都要 CAS”,此设计完全消除了重入的性能开销。
(3)偏向锁撤销
当有第二个线程尝试获取锁时,偏向锁会被撤销(需等待全局安全点):
1.JVM 暂停持有偏向锁的线程,遍历其栈帧中的所有 Lock Record,清空与当前锁对象绑定的 Lock Record;
2.撤销后有两种结果:
若原持有线程已退出同步代码:将锁恢复为无锁状态;
若原持有线程仍在执行:升级为轻量级锁,原线程的 Lock Record 会填充锁对象的 Mark Word 副本。
3. 适用场景
- 单线程重复获取同一把锁的场景(如单线程循环调用同步方法)。
- 无任何线程竞争,性能开销几乎为 0。
四、轻量级锁:自旋解决短时间竞争
1. 核心思想
轻量级锁适用于多个线程交替获取锁(短时间竞争)的场景,通过自旋(用户态循环)代替线程阻塞,避免内核态切换的开销。
“轻量级” 体现在:无需调用操作系统的互斥量,完全在用户态完成锁的获取 / 释放,而 Lock Record 是轻量级锁的核心载体。
(1)加锁过程
1.创建 Lock Record:线程进入同步代码块时,JVM 会在该线程的栈帧中创建一个 Lock Record,核心字段包括:

obj:指向锁对象的引用;displaced_mark_word:存储锁对象当前 Mark Word 的副本(区别于偏向锁的空字段)。
注意注意:存储锁对象当前 Mark Word 的副本有什么作用?
其实就是为了解锁时需要把对象头恢复成原来的样子,副本就是 “原来的样子” 的唯一凭证。
相当于是恢复现场
2.CAS 替换 Mark Word:线程通过 CAS 操作,将锁对象的 Mark Word 替换为指向栈中 Lock Record 的指针:

- CAS 成功:轻量级锁加锁成功,Mark Word 标志位改为 00。
- CAS 失败:说明有其他线程竞争,进入自旋。
(2)重入、自旋与升级
- 如果是当前线程已经持有该锁了,代表这是一次锁重入。设置Lock Record第一部分为null,起到了一个重入计数器的作用。

- 自旋:如果锁已经被持有,其他线程不会立即阻塞,而是循环尝试获取锁(默认自旋 10 次,或自适应自旋)。
- 升级时机:
- 自旋成功:获取锁,执行同步代码。
- 自旋失败(超过自旋次数 / 其他线程已获取锁):轻量级锁升级为重量级锁。
(3)解锁过程
线程执行完同步代码后,通过 CAS 将 Lock Record 中备份的 displaced_mark_word 恢复到锁对象的 Mark Word 中:
- CAS 成功:解锁完成,对象回到无锁状态;
- CAS 失败:说明锁已升级为重量级锁,需触发 Monitor 机制唤醒阻塞线程。
3. 偏向锁 vs 轻量级锁的 Lock Record 区别
| 维度 | 偏向锁的 Lock Record | 轻量级锁的 Lock Record |
|---|---|---|
| 核心字段 | obj 指向锁对象,重入时字段为 null |
obj 指向锁对象,存储 Mark Word 副本 |
| 核心操作 | 仅首次 CAS,重入无 CAS | 加锁 / 重入均需 CAS 操作 |
| 性能开销 | 几乎为 0 | 低(用户态 CAS + 自旋) |
4. 适用场景
- 多线程短时间交替竞争锁(如循环次数少的同步代码)。
- 自旋能减少线程阻塞的开销,但若自旋过久,反而会浪费 CPU 资源。
五、重量级锁:Monitor 机制兜底
1. 核心思想
当锁竞争激烈(多个线程长时间竞争)时,轻量级锁升级为重量级锁,依赖操作系统的 Mutex 和 JVM 的 Monitor 机制实现互斥。
2. 实现原理
- 轻量级锁升级为重量级锁时,JVM 会为对象创建一个 Monitor(监视器),并将 Mark Word 改为指向 Monitor 的指针(标志位 10);
- 竞争失败的线程不再自旋,而是进入 Monitor 的
_EntryList队列阻塞(切换到内核态);- 持有锁的线程释放锁时,唤醒
_EntryList中的线程重新竞争锁;- 关键:重量级锁阶段,线程栈中的 Lock Record 不再起核心作用,锁的管理完全交给 Monitor。
3. 优缺点
- 缺点:线程阻塞 / 唤醒需要切换内核态,开销大。
- 优点:适合长时间竞争的场景,避免自旋浪费 CPU 资源。
这里其实就是我上一篇文章所写的
六、锁升级完整流程

- 初始状态:对象为无锁状态,Mark Word 存储哈希码 + 分代年龄 + 001 标志位。
- 偏向锁:第一个线程创建 Lock Record,通过 CAS 将线程 ID 写入 Mark Word(标志位 101),重入时仅创建 null 的 Lock Record(无 CAS)。
- 轻量级锁:第二个线程竞争导致偏向锁撤销,升级为轻量级锁,线程创建存储 Mark Word 副本的 Lock Record,通过 CAS + 自旋竞争锁。
- 重量级锁:自旋失败 / 竞争加剧,升级为重量级锁,Lock Record 失效,依赖 Monitor 机制实现互斥。
关键:锁升级是单向的(只能从低到高),不会降级(如重量级锁不会变回轻量级锁),避免频繁切换锁状态带来的开销。
感兴趣的宝子可以关注一波,后续会更新更多有用的知识!!!

更多推荐




所有评论(0)