首页 > JAVA > 当前页面

Java锁升级机制详解:偏向锁、轻量级锁、重量级锁是如何一步步升级的?

2026-06-10 NEW个对象

🔥 Java锁升级机制详解:偏向锁、轻量级锁、重量级锁是如何一步步升级的?

📌 核心结论:
Java中的synchronized并不是一开始就使用重量级锁。为了减少线程竞争带来的性能损耗,HotSpot JVM设计了锁升级机制。锁会根据竞争情况经历无锁 → 偏向锁 → 轻量级锁 → 重量级锁的升级过程。锁只能升级不能降级,从而保证线程安全并提高系统吞吐量。

1️⃣ 问题背景

在JDK1.5之前,synchronized一直被认为是重量级锁。

每次线程进入同步代码块都需要向操作系统申请Mutex互斥量。

线程阻塞和唤醒涉及用户态和内核态切换。

这种切换代价非常高。

例如:

synchronized(lock){
  //业务逻辑
}

即使只有一个线程执行,也会触发重量级锁逻辑。

显然存在大量性能浪费。

⚠️ 大部分业务场景实际上并不存在激烈竞争,因此JVM引入了锁升级机制。

从JDK1.6开始,HotSpot对synchronized进行了大量优化。

  • 偏向锁
  • 轻量级锁
  • 自旋锁
  • 锁消除
  • 锁粗化

其中最核心的就是锁升级机制。

2️⃣ 核心原理

锁升级本质上是根据竞争程度动态调整锁状态。

竞争越小,锁越轻。

竞争越大,锁越重。

无锁

偏向锁

轻量级锁

重量级锁

升级过程依赖对象头中的Mark Word实现。

Mark Word记录了对象运行时状态。

  • HashCode
  • GC年龄
  • 锁标志位
  • 线程ID
  • 偏向锁状态

JVM通过修改Mark Word中的标志位完成锁状态切换。

💡 面试核心回答:锁升级本质是Mark Word不断变化的过程。

3️⃣ 数据结构分析

对象头结构

HotSpot对象主要由对象头和实例数据组成。

Object
├── Mark Word
└── Klass Pointer

锁状态信息主要保存在Mark Word中。

无锁状态

Mark Word

HashCode
GC Age
Lock Flag

对象刚创建时处于无锁状态。

此时没有线程持有锁。

偏向锁状态

偏向锁会记录线程ID。

Mark Word

ThreadId
Epoch
BiasedFlag

如果后续还是同一个线程访问同步块。

无需执行CAS竞争。

轻量级锁状态

轻量级锁会创建Lock Record。

线程栈

Lock Record

CAS替换Mark Word

多个线程竞争时进入轻量级锁阶段。

重量级锁状态

竞争进一步加剧。

JVM创建Monitor对象。

Object

Monitor

EntryList

WaitSet

未获得锁的线程进入阻塞状态。

4️⃣ 算法分析

偏向锁算法

偏向锁假设大部分时间只有一个线程访问同步块。

第一次获取锁时:

MarkWord.ThreadId = 当前线程ID

后续进入同步块:

if(ThreadId == 当前线程){
  直接进入
}

无需CAS。

无需加锁。

效率最高。

轻量级锁算法

当第二个线程尝试获取锁时。

偏向锁撤销。

升级轻量级锁。

CAS(MarkWord,LockRecord)

CAS成功:

获得锁。

CAS失败:

进入自旋阶段。

自旋锁算法

线程不会立即阻塞。

而是在CPU中循环尝试获取锁。

while(lock != null){
  retry();
}

如果短时间内锁被释放。

即可直接获得锁。

避免线程切换开销。

重量级锁算法

自旋次数达到阈值。

或者竞争线程持续增多。

锁膨胀为重量级锁。

park(thread)
unpark(thread)

线程进入阻塞队列。

等待唤醒。

5️⃣ 执行流程

对象创建

无锁状态

线程A进入同步块

升级偏向锁

线程A再次进入

直接执行

线程B开始竞争

撤销偏向锁

升级轻量级锁

CAS竞争

竞争激烈

自旋重试

持续失败

升级重量级锁

线程阻塞等待

6️⃣ 实际案例

案例一:单线程场景

public void test(){
  synchronized(this){
    //业务代码
  }
}

只有一个线程访问。

锁升级为偏向锁。

后续进入同步块无需竞争。

案例二:两个线程竞争

ThreadA
ThreadB

ThreadA已经持有偏向锁。

ThreadB到来后。

偏向锁撤销。

升级轻量级锁。

双方通过CAS竞争。

案例三:高并发竞争

100个线程
同时访问同步块

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重量级锁,从而在保证线程安全的同时最大程度提升并发性能。

相关文章

NEW个对象 NEW个对象
JAVA是世界上最好的语言

推荐文章