在 Java 中,synchronized 的锁升级是 JVM 对其进行的性能优化机制(从 JDK 6 开始引入)。
其核心思想是:根据线程竞争的激烈程度,让锁的实现从 “轻量级” 逐渐升级到 “重量级”,避免在低竞争场景下使用重量级锁带来的性能开销(如操作系统内核态切换)。
一、锁升级的背景:为什么需要升级?
早期的 synchronized 直接使用重量级锁(依赖操作系统的互斥量 mutex),但重量级锁的问题在于:
- 线程获取 / 释放锁时需要从用户态切换到内核态,开销大。
- 即使在只有一个线程访问(无竞争)或轻度竞争的场景,也会产生不必要的性能损耗。
为解决这个问题,JVM 引入了锁升级机制:让锁根据竞争程度动态转换状态(无锁 → 偏向锁 → 轻量级锁 → 重量级锁),在低竞争时用轻量实现,高竞争时才用重量级锁,平衡性能和安全性。
二、锁的四种状态及升级流程
synchronized 的锁状态完全依赖对象头的 Mark Word(对象头的一部分,存储对象的元数据)。Mark Word 的结构会随锁状态变化,以下是具体状态及升级逻辑:
1. 无锁状态(初始状态)
- 适用场景:对象刚创建,未被任何线程锁定。
- Mark Word 存储内容:对象的哈希码、分代年龄、是否为偏向锁标记(0)等。
- 示例:
Object obj = new Object(); 此时 obj 处于无锁状态。
2. 偏向锁(单线程竞争)
- 适用场景:只有一个线程多次获取同一把锁(无多线程竞争)。
- 核心目标:消除无竞争时的同步开销(如 CAS 操作)。
偏向锁的获取过程:
- 线程第一次获取锁时,JVM 会通过 CAS 操作,将当前线程 ID 写入对象的 Mark Word,并将 “偏向锁标记” 设为 1。
- 后续该线程再次获取锁时,无需 CAS 操作,只需简单判断:
- Mark Word 中的线程 ID 是否为当前线程 ID?
- 偏向锁标记是否为 1?
若均满足,直接获取锁成功(几乎零开销)。
偏向锁的撤销(升级触发):
当有其他线程尝试获取锁时,偏向锁会被 “撤销”,升级为轻量级锁。撤销过程需要暂停持有偏向锁的线程,并根据其状态(是否在执行同步块)处理:
- 若原线程已退出同步块,直接将 Mark Word 恢复为无锁状态,新线程竞争轻量级锁。
- 若原线程仍在执行,将偏向锁升级为轻量级锁,原线程继续执行,新线程自旋尝试获取。
特殊说明:
- 偏向锁默认延迟启动(JVM 启动后约 4 秒),避免启动时大量对象初始化导致的频繁偏向锁撤销。可通过
XX:-UseBiasedLocking 禁用偏向锁,或 XX:BiasedLockingStartupDelay=0 取消延迟。
3. 轻量级锁(轻度竞争)
- 适用场景:多个线程交替获取锁(竞争不激烈,无线程长时间阻塞)。
- 核心目标:通过自旋(用户态循环)避免升级为重量级锁(内核态阻塞)。
轻量级锁的获取过程:
-
线程进入同步块时,JVM 会在当前线程的栈帧中创建一个 Lock Record(锁记录),存储对象 Mark Word 的拷贝(称为 Displaced Mark Word)。
-
线程通过 CAS 操作,将对象的 Mark Word 替换为
指向当前 Lock Record 的指针:
- 若 CAS 成功,线程获取轻量级锁,执行同步逻辑。
- 若 CAS 失败(说明其他线程已持有轻量级锁),则当前线程进入自旋(循环尝试 CAS),等待锁释放。
轻量级锁的释放过程:
线程退出同步块时,通过 CAS 将 Displaced Mark Word 写回对象的 Mark Word:
- 若 CAS 成功,释放锁成功。
- 若 CAS 失败(说明锁已升级为重量级锁),则释放锁并唤醒阻塞的线程。
轻量级锁的升级触发:
当自旋超过一定次数(JVM 自适应调整,通常为 10 次),或自旋线程数超过 CPU 核心数的一半时,JVM 认为竞争已 “激烈”,轻量级锁会升级为重量级锁。
4. 重量级锁(重度竞争)
- 适用场景:多个线程同时争抢锁(竞争激烈,自旋无法解决)。
- 核心目标:通过操作系统的互斥量保证线程安全,避免自旋消耗 CPU。
重量级锁的实现:
- 锁升级为重量级后,对象的 Mark Word 会指向一个操作系统级别的互斥量(mutex)。
- 未获取到锁的线程会直接进入阻塞状态(放弃 CPU 资源),由操作系统放入等待队列。
- 持有锁的线程释放锁时,会通过操作系统唤醒等待队列中的线程,重新竞争锁。
缺点:
线程从阻塞到唤醒需要用户态 ↔ 内核态切换,开销较大,但在重度竞争场景下,比自旋(空耗 CPU)更高效。
三、锁升级的完整流程总结
- 初始状态:对象为无锁状态,Mark Word 存储哈希码等信息。
- 单线程访问:升级为偏向锁,Mark Word 记录当前线程 ID,后续访问零开销。
- 多线程交替访问(轻度竞争):偏向锁撤销,升级为轻量级锁,线程通过自旋 CAS 竞争锁。
- 多线程同时争抢(重度竞争):轻量级锁升级为重量级锁,线程阻塞等待,依赖操作系统互斥量。
四、核心总结
synchronized 的锁升级是 JVM 对 “不同竞争强度” 的自适应优化:
- 低竞争(单线程):用偏向锁,消除 CAS 开销。
- 中竞争(交替访问):用轻量级锁,自旋避免内核态切换。
- 高竞争(同时争抢):用重量级锁,阻塞等待节省 CPU。
这种机制让 synchronized 在各种场景下都能保持较好的性能,也是现代 JVM 中 synchronized 性能接近 ReentrantLock 的重要原因。