Java高频面试之并发编程-19
hello啊,各位观众姥爷们!!!本baby今天又来报道了!哈哈哈哈哈嗝🐶
面试官:synchronized锁升级是什么?
在 Java 中,synchronized
锁升级是 JVM 为了优化同步性能而引入的机制(JDK 1.6+)。其核心思想是:根据线程竞争锁的激烈程度,动态调整锁的级别,从低开销的锁逐步升级到高开销的锁,从而减少不必要的性能损耗。
锁升级的四个阶段
锁的升级过程遵循以下顺序(不可逆):
无锁 → 偏向锁 → 轻量级锁 → 重量级锁
1. 无锁(No Lock)
- 场景:对象未被任何线程锁定。
- 特点:
- 初始状态,无同步需求。
- 适合线程不访问共享资源的场景(如只读操作)。
2. 偏向锁(Biased Lock)
- 场景:锁始终被同一个线程访问(无竞争)。
- 原理:
- 在对象头中记录偏向的线程 ID(通过 CAS 操作)。
- 后续该线程进入同步代码时,无需任何同步操作(直接访问)。
- 优点:消除无竞争时的锁开销。
- 升级条件:
- 当其他线程尝试竞争偏向锁时,偏向锁撤销(Revoke),升级为轻量级锁。
3. 轻量级锁(Lightweight Lock)
- 场景:锁被多个线程交替访问(低竞争)。
- 原理:
- 线程通过 CAS 自旋尝试获取锁(不会立即阻塞)。
- 若自旋成功,对象头中存储指向锁记录的指针(Lock Record)。
- 若自旋失败(超过阈值),升级为重量级锁。
- 优点:减少线程阻塞带来的上下文切换开销。
- 升级条件:
- 自旋失败(竞争加剧),或等待线程超过一定数量(默认自旋 10 次或自适应调整)。
4. 重量级锁(Heavyweight Lock)
- 场景:多线程高竞争。
- 原理:
- 锁由 JVM 通过操作系统级 互斥量(Mutex) 实现。
- 未获取锁的线程直接进入阻塞状态(BLOCKED),等待唤醒。
- 优点:避免 CPU 空转(自旋浪费资源)。
- 缺点:线程阻塞和唤醒涉及内核态切换,性能开销大。
锁升级流程示意图
无锁 → (线程首次访问) → 偏向锁 → (出现竞争) → 轻量级锁 → (竞争激烈) → 重量级锁
锁升级的底层实现
-
对象头(Object Header):
锁状态信息存储在对象头的 Mark Word 中,包含锁标志位、线程 ID、锁记录指针等。- 32 位 JVM 的 Mark Word 结构示例:
| 锁状态 | 25 bits | 4 bits | 1 bit (偏向锁) | 2 bits (锁标志) | |----------|------------------|----------------|----------------|----------------| | 无锁 | 对象的 hashCode | 分代年龄 | 0 | 01 | | 偏向锁 | 线程ID + Epoch | 分代年龄 | 1 | 01 | | 轻量级锁 | 指向锁记录的指针 | | | 00 | | 重量级锁 | 指向 Monitor 的指针 | | | 10 |
- 32 位 JVM 的 Mark Word 结构示例:
-
锁升级触发:
通过 JVM 内置的优化策略(如竞争检测、自旋次数统计)动态调整。
锁升级的意义
-
性能优化:
- 避免无竞争或低竞争时直接使用重量级锁的开销。
- 偏向锁和轻量级锁通过 CAS 和自旋减少线程阻塞。
-
适应性:
- 根据实际竞争强度动态调整,平衡吞吐量和响应时间。
-
兼容性:
- 保持
synchronized
的语法简洁性,开发者无需手动优化锁级别。
- 保持
示例:锁升级过程
public class LockUpgradeExample {private static final Object lock = new Object();public static void main(String[] args) {// 线程1首次访问:偏向锁new Thread(() -> {synchronized (lock) {System.out.println("Thread1 获取锁");}}).start();// 线程2竞争:升级为轻量级锁new Thread(() -> {synchronized (lock) {System.out.println("Thread2 获取锁");}}).start();// 高竞争场景:最终升级为重量级锁for (int i = 0; i < 10; i++) {new Thread(() -> {synchronized (lock) {try { Thread.sleep(100); } catch (InterruptedException e) {}}}).start();}}
}
注意事项
-
锁降级:
- JVM 不会主动降级锁(如重量级锁不会回退到轻量级锁)。
- 降级仅在某些特殊场景下发生(如全局安全点检查)。
-
偏向锁延迟:
- JVM 默认在启动后 4 秒才启用偏向锁(通过
-XX:BiasedLockingStartupDelay=0
可关闭延迟)。
- JVM 默认在启动后 4 秒才启用偏向锁(通过
-
禁用锁升级:
- 通过 JVM 参数控制(如
-XX:-UseBiasedLocking
禁用偏向锁)。
- 通过 JVM 参数控制(如