Java锁升级机制详解:偏向锁、轻量级锁、重量级锁是如何一步步升级的?
🔥 Java锁升级机制详解:偏向锁、轻量级锁、重量级锁是如何一步步升级的?
Java中的synchronized并不是一开始就使用重量级锁。为了减少线程竞争带来的性能损耗,HotSpot JVM设计了锁升级机制。锁会根据竞争情况经历无锁 → 偏向锁 → 轻量级锁 → 重量级锁的升级过程。锁只能升级不能降级,从而保证线程安全并提高系统吞吐量。
1️⃣ 问题背景
在JDK1.5之前,synchronized一直被认为是重量级锁。
每次线程进入同步代码块都需要向操作系统申请Mutex互斥量。
线程阻塞和唤醒涉及用户态和内核态切换。
这种切换代价非常高。
例如:
//业务逻辑
}
即使只有一个线程执行,也会触发重量级锁逻辑。
显然存在大量性能浪费。
从JDK1.6开始,HotSpot对synchronized进行了大量优化。
- 偏向锁
- 轻量级锁
- 自旋锁
- 锁消除
- 锁粗化
其中最核心的就是锁升级机制。
2️⃣ 核心原理
锁升级本质上是根据竞争程度动态调整锁状态。
竞争越小,锁越轻。
竞争越大,锁越重。
↓
偏向锁
↓
轻量级锁
↓
重量级锁
升级过程依赖对象头中的Mark Word实现。
Mark Word记录了对象运行时状态。
- HashCode
- GC年龄
- 锁标志位
- 线程ID
- 偏向锁状态
JVM通过修改Mark Word中的标志位完成锁状态切换。
3️⃣ 数据结构分析
对象头结构
HotSpot对象主要由对象头和实例数据组成。
├── Mark Word
└── Klass Pointer
锁状态信息主要保存在Mark Word中。
无锁状态
↓
HashCode
GC Age
Lock Flag
对象刚创建时处于无锁状态。
此时没有线程持有锁。
偏向锁状态
偏向锁会记录线程ID。
↓
ThreadId
Epoch
BiasedFlag
如果后续还是同一个线程访问同步块。
无需执行CAS竞争。
轻量级锁状态
轻量级锁会创建Lock Record。
↓
Lock Record
↓
CAS替换Mark Word
多个线程竞争时进入轻量级锁阶段。
重量级锁状态
竞争进一步加剧。
JVM创建Monitor对象。
↓
Monitor
↓
EntryList
↓
WaitSet
未获得锁的线程进入阻塞状态。
4️⃣ 算法分析
偏向锁算法
偏向锁假设大部分时间只有一个线程访问同步块。
第一次获取锁时:
后续进入同步块:
直接进入
}
无需CAS。
无需加锁。
效率最高。
轻量级锁算法
当第二个线程尝试获取锁时。
偏向锁撤销。
升级轻量级锁。
CAS成功:
获得锁。
CAS失败:
进入自旋阶段。
自旋锁算法
线程不会立即阻塞。
而是在CPU中循环尝试获取锁。
retry();
}
如果短时间内锁被释放。
即可直接获得锁。
避免线程切换开销。
重量级锁算法
自旋次数达到阈值。
或者竞争线程持续增多。
锁膨胀为重量级锁。
unpark(thread)
线程进入阻塞队列。
等待唤醒。
5️⃣ 执行流程
↓
无锁状态
↓
线程A进入同步块
↓
升级偏向锁
↓
线程A再次进入
↓
直接执行
↓
线程B开始竞争
↓
撤销偏向锁
↓
升级轻量级锁
↓
CAS竞争
↓
竞争激烈
↓
自旋重试
↓
持续失败
↓
升级重量级锁
↓
线程阻塞等待
6️⃣ 实际案例
案例一:单线程场景
synchronized(this){
//业务代码
}
}
只有一个线程访问。
锁升级为偏向锁。
后续进入同步块无需竞争。
案例二:两个线程竞争
ThreadB
ThreadA已经持有偏向锁。
ThreadB到来后。
偏向锁撤销。
升级轻量级锁。
双方通过CAS竞争。
案例三:高并发竞争
同时访问同步块
CAS大量失败。
自旋消耗CPU严重。
JVM自动膨胀为重量级锁。
线程进入Monitor等待队列。
7️⃣ 优缺点分析
| 锁状态 | 优点 | 缺点 |
|---|---|---|
| 偏向锁 | 几乎无开销 | 多线程竞争时需撤销 |
| 轻量级锁 | 避免线程阻塞 | CAS失败消耗CPU |
| 自旋锁 | 减少上下文切换 | 长时间竞争浪费CPU |
| 重量级锁 | 保证公平竞争 | 性能最差 |
8️⃣ 面试常见问题
synchronized底层是什么实现?
依赖对象头Mark Word和Monitor对象实现。
锁升级过程是什么?
无锁 → 偏向锁 → 轻量级锁 → 重量级锁。
锁为什么只能升级不能降级?
避免频繁转换带来的性能损耗和状态复杂度。
偏向锁为什么快?
只记录线程ID,不需要CAS操作。
轻量级锁为什么使用CAS?
利用乐观锁思想减少线程阻塞。
什么时候会升级重量级锁?
CAS竞争失败次数过多或者线程竞争激烈时。
JDK15以后还有偏向锁吗?
JDK15默认关闭偏向锁。
JDK18开始已经彻底移除偏向锁实现。
Monitor是什么?
Monitor是HotSpot实现重量级锁的核心对象,内部维护Owner、EntryList和WaitSet等结构。
9️⃣ 总结
✅ synchronized并非一开始就是重量级锁。
✅ JVM通过锁升级机制实现性能优化。
✅ 锁升级过程为无锁、偏向锁、轻量级锁、重量级锁。
✅ 偏向锁适合单线程访问场景。
✅ 轻量级锁依赖CAS竞争实现。
✅ 自旋锁通过忙等待减少线程切换。
✅ 竞争激烈时最终升级为重量级锁。
✅ 锁升级过程本质上是Mark Word状态不断变化的过程。
Java锁升级机制是JVM对synchronized的重要优化,锁会根据竞争情况从无锁升级为偏向锁、轻量级锁以及重量级锁。其中偏向锁利用线程ID避免CAS,轻量级锁利用CAS避免阻塞,竞争激烈时升级为Monitor重量级锁,从而在保证线程安全的同时最大程度提升并发性能。
相关文章
-
JVM常见的配置参数
JVM常见的配置参数
NEW个对象 2025-01-09
-
sleep与wait的区别
1、sleep()方法属于Thread类的静态方法,wait()方法属于Object类的实例方法
NEW个对象 2025-01-10
-
什么是反射机制?为什么慢?
反射机制是指在运行时能获取到自身信息,只要给出类名,就可以访问类的属性和方法。
NEW个对象 2025-01-09