synchronized锁升级过程【AI笔记,仅供自己参考】
在 Java 中,synchronized
是一种内置的同步机制,用于保证多线程环境下代码的原子性、可见性和有序性。从 JDK 1.6 开始,为了减少锁带来的性能开销,Java 对 synchronized
做了大量优化,引入了 锁升级机制(Lock Escalation)。
🔐 一、什么是锁升级?
锁升级是指 JVM 在运行时根据对象的使用情况,对对象的锁状态进行动态优化的过程。它不是“升级为更重的锁”,而是从轻量级锁逐步升级到重量级锁的过程,以在不同竞争程度下取得性能平衡。
🧱 二、锁的类型(对象头中的 Mark Word)
在 Java 中,每个对象都有一个对象头(Object Header),其中的 Mark Word 保存了对象的哈希码、GC 分代年龄、锁状态等信息。锁的升级就体现在 Mark Word 的变化上。
锁的状态分为以下几种:
锁状态 | 描述 |
---|---|
无锁状态(Unlocked) | 对象未被任何线程锁定 |
偏向锁(Biased Locking) | 只有一个线程访问同步块时使用 |
轻量级锁(Lightweight Locking) | 多个线程交替执行同步块,没有竞争 |
重量级锁(Inflated Lock) | 多个线程发生竞争,锁膨胀为 OS 层面的互斥量(Mutex) |
🔁 三、锁升级流程(从无锁到重量级锁)
深色版本
无锁状态│▼
偏向锁(只有一个线程)│(有竞争)▼
轻量级锁(CAS 尝试加锁)│(竞争激烈)▼
重量级锁(阻塞线程)
1. 无锁状态
- 初始状态,对象未被任何线程锁定。
- Mark Word 中记录的是对象的 hash、GC 信息等。
2. 偏向锁(Biased Locking)
- 当一个线程访问同步块并获取锁时,JVM 会将对象头中的 Mark Word 设置为该线程 ID。
- 表示这个对象“偏向”这个线程。
- 优点:避免了无竞争情况下的同步开销。
- 缺点:当有其他线程尝试获取锁时,偏向锁会失效,需要撤销(Revoke)并升级为轻量级锁。
✅ 偏向锁默认是开启的(JDK 6~15),但可以通过 JVM 参数关闭:
-XX:-UseBiasedLocking
3. 轻量级锁(Lightweight Locking)
- 多个线程交替执行同步块,没有实际竞争。
- 使用 CAS(Compare and Swap)操作尝试将对象头的 Mark Word 替换为指向线程栈中锁记录的指针。
- 如果 CAS 成功,则获得锁;失败则说明有竞争,进入锁膨胀。
4. 重量级锁(Inflated Lock)
- 多个线程发生竞争时,锁会膨胀为 OS 层面的互斥锁(Mutex)。
- JVM 会把线程阻塞,并等待操作系统调度。
- 性能开销最大,因为涉及到线程上下文切换和系统调用。
🧪 四、锁升级的触发条件
条件 | 锁升级 |
---|---|
第一次加锁 | 无锁 → 偏向锁 |
有线程竞争(偏向锁) | 偏向锁 → 轻量级锁 |
CAS 失败(轻量级锁) | 轻量级锁 → 重量级锁 |
📈 五、锁升级的性能影响
锁类型 | 性能开销 | 适用场景 |
---|---|---|
偏向锁 | ✅ 最低 | 单线程访问同步块 |
轻量级锁 | ⚠️ 较低 | 多线程交替访问 |
重量级锁 | ❌ 最高 | 多线程竞争激烈 |
🧪 六、代码示例演示锁升级过程
public class SynchronizedLockUpgrade {public static void main(String[] args) throws InterruptedException {Object obj = new Object();new Thread(() -> {synchronized (obj) {System.out.println("Thread 1: holding lock");printMarkWord(obj);}}, "Thread-1").start();Thread.sleep(1000); // 确保 Thread-1 执行完偏向锁new Thread(() -> {synchronized (obj) {System.out.println("Thread 2: trying to get lock");printMarkWord(obj);}}, "Thread-2").start();}// 通过 JVM 工具打印对象头(Mark Word)private static void printMarkWord(Object obj) {// 实际中使用 JOL(Java Object Layout)库查看对象头// 示例代码略}
}
⚠️ 说明:要查看对象头(Mark Word)的变化,可以使用 JOL(Java Object Layout) 工具。
🛠️ 七、优化建议
场景 | 建议 |
---|---|
无并发竞争 | 启用偏向锁,减少同步开销 |
短期竞争 | 使用轻量级锁 |
长期竞争 | 使用重量级锁或 ReentrantLock |
高并发场景 | 考虑使用 ReentrantLock 或分段锁(如 ConcurrentHashMap ) |
不需要同步 | 避免使用 synchronized ,使用 volatile 或原子类 |
📚 八、相关 JVM 参数(了解)
参数 | 说明 |
---|---|
-XX:+UseBiasedLocking | 启用偏向锁(默认开启) |
-XX:BiasedLockingStartupDelay=0 | 提前启用偏向锁 |
-XX:-UseBiasedLocking | 禁用偏向锁 |
-XX:+PrintCommandLineFlags | 查看当前 JVM 使用的参数 |
✅ 九、总结:synchronized 的锁升级过程
步骤 | 锁类型 | 触发条件 |
---|---|---|
1 | 无锁状态 | 初始状态 |
2 | 偏向锁 | 第一次加锁(单线程) |
3 | 轻量级锁 | 多线程交替访问(CAS) |
4 | 重量级锁 | 线程竞争激烈(锁膨胀) |
📌 十、延伸阅读
- Java Object Layout (JOL)
- 《深入理解 Java 虚拟机》——周志明(第3版)
- JVM 源码分析:
synchronizer.cpp
和objectMonitor.cpp