轻量级锁 vs 重量级锁:核心区别与设计哲学
在JVM的锁优化体系中,轻量级锁和重量级锁是两种不同竞争强度下的解决方案。它们的核心差异体现在 资源消耗、适用场景和实现机制 上。以下是详细对比:
一、轻量级锁(Thin Lock)
1. 设计目标
• 优化场景:针对 低竞争 或 短时间持有锁 的情况(如两个线程交替访问同步块)。
• 核心思想:通过 CAS自旋 避免线程直接阻塞,减少用户态到内核态的切换开销。
2. 实现原理
• 锁记录(Lock Record):
线程在栈帧中分配一个Lock Record
空间,存储锁对象的Mark Word副本。
• CAS竞争:
通过CAS操作尝试将锁对象的Mark Word替换为指向Lock Record
的指针。
• 成功:线程获取锁,Mark Word标志位变为轻量级锁(00)。
• 失败:触发 自旋等待 或 锁膨胀(升级为重量级锁)。
3. "轻"在哪里?
维度 | 轻量级锁 | 原因 |
---|---|---|
资源消耗 | 仅需栈帧中的Lock Record 和少量CAS操作 | 不涉及内核态切换,无线程阻塞 |
性能开销 | 纳秒级自旋(CPU空转) | 自旋时间短(JDK6后自适应调整),避免上下文切换 |
适用场景 | 线程交替执行同步块(如短任务、低并发) | 竞争概率低,自旋成功率高 |
4. 示例流程
Object obj = new Object();
synchronized (obj) { // 1. 栈帧创建Lock Record
// 2. CAS替换Mark Word
// 3. 成功则进入同步块
}
二、重量级锁(Heavyweight Lock)
1. 设计目标
• 处理高竞争:当多个线程激烈争抢同一锁时(如长时间持有锁或高并发场景)。
• 核心机制:通过操作系统提供的 互斥量(Mutex) 和 条件变量 实现线程阻塞与唤醒。
2. 实现原理
• Monitor对象:
每个Java对象关联一个ObjectMonitor
(C++实现),包含:
• _owner
:持有锁的线程
• _EntryList
:阻塞等待锁的线程队列
• _WaitSet
:调用wait()
的线程队列
• 内核介入:
线程竞争失败后,直接进入阻塞状态(通过pthread_mutex_lock
),由操作系统调度唤醒。
3. "重"在哪里?
维度 | 重量级锁 | 原因 |
---|---|---|
资源消耗 | 需要维护ObjectMonitor 和内核级线程调度 | 涉及用户态→内核态切换,系统调用开销大 |
性能开销 | 微秒级线程阻塞与唤醒 | 线程阻塞导致上下文切换(CPU寄存器和堆栈保存/恢复) |
适用场景 | 长时间持有锁或高并发竞争(如数据库连接池) | 自旋代价过高,直接阻塞更高效 |
4. 示例流程
synchronized (highContentionLock) { // 1. 竞争失败后加入_EntryList
// 2. 线程被OS挂起
// 3. 锁释放时唤醒等待线程
}
三、关键对比总结
特性 | 轻量级锁 | 重量级锁 |
---|---|---|
锁标志位 | 00(Mark Word中) | 10(Mark Word中) |
竞争处理 | CAS自旋(用户态) | 线程阻塞(内核态) |
资源占用 | 栈帧中的Lock Record | ObjectMonitor +内核队列 |
性能开销 | 低(自旋消耗CPU周期) | 高(上下文切换、系统调用) |
升级触发条件 | 自旋失败或竞争激烈 | 轻量级锁膨胀或显式调用wait() |
四、锁升级的全流程
1. 无锁 → 偏向锁(单线程访问)
└─ 出现第二个线程 → 撤销偏向锁
2. 轻量级锁(CAS自旋)
└─ 自旋失败/长时间竞争 → 膨胀为重量级锁
3. 重量级锁(OS互斥量)
五、实际场景与调优建议
-
优先使用轻量级锁
• 适用场景:短任务、低并发(如局部变量的同步块)。
• 调优参数:-XX:PreBlockSpin=10
(调整自旋次数,JDK6后自适应无需手动设置)。 -
避免重量级锁的滥用
• 问题:高竞争时频繁阻塞/唤醒会导致性能骤降。
• 解决方案:
◦ 减小锁粒度(如ConcurrentHashMap
分段锁)。
◦ 改用无锁结构(如AtomicLong
)。 -
监控锁状态
• 工具:
◦ JOL(Java Object Layout)查看对象头:
java System.out.println(ClassLayout.parseInstance(obj).toPrintable());
◦ JConsole检查锁竞争:监控线程阻塞和等待时间。
总结
• 轻量级锁的"轻":体现在用户态自旋、低资源占用,适合短暂竞争。
• 重量级锁的"重":体现在内核态阻塞、高系统开销,应对高并发竞争。
• 设计哲学:JVM通过锁升级机制(偏向→轻量级→重量级)实现 按需分配资源,平衡性能与安全性。理解这一机制有助于编写高效并发代码,并在性能调优时精准定位锁瓶颈。