Java锁机制:从偏向锁到重量级锁的动态升级
一、synchronized锁的底层实现:对象头与Mark Word
synchronized是 Java 内置的锁机制,其底层实现是基于**Java对象的对象头(Object Header)**中的 Mark Word。Mark Word 用于存储对象的运行时数据,包括锁状态、哈希码、GC分代年龄等。
- 锁状态:
Mark Word中有一个用于标记锁状态的位。通过修改这个位,JVM 可以实现锁的动态升级。
二、锁的升级:根据竞争情况动态调整
synchronized 锁的升级是一个不可逆的过程,旨在在无竞争、低竞争和高竞争场景下,提供最佳的性能。
1. 无锁状态
- 特点:
Mark Word中只存储对象的哈希码、分代年龄等信息。 - 性能:无任何锁开销。
2. 偏向锁(Biased Locking)
- 核心思想:当一个线程首次获取锁时,JVM 会在
Mark Word中记录该线程的 ID。 - 适用场景:无竞争,同一线程重复获取锁的场景。
- 性能:后续该线程再次获取锁时,只需比对
Mark Word中的线程 ID,无需额外的同步开销,性能极高。 - 撤销:当另一个线程试图获取已被偏向的锁时,偏向锁会被撤销,并升级为轻量级锁。
3. 轻量级锁(Lightweight Locking)
- 核心思想:当偏向锁被撤销后,JVM 会在栈帧中创建一个锁记录,并通过 CAS(Compare-And-Swap)操作将对象头中的
Mark Word指向锁记录。 - 适用场景:低竞争,线程交替获取锁的场景。
- 性能:线程在获取锁失败时,会进行自旋,而不是立即阻塞。这避免了线程阻塞和唤醒的开销。
- 升级:当自旋失败,或者锁竞争激烈时,轻量级锁会升级为重量级锁。
4. 重量级锁(Heavyweight Locking)
- 核心思想:当锁竞争激烈时,线程会阻塞。
synchronized的重量级锁是基于操作系统的 **Mutex(互斥量)**实现的。 - 适用场景:高竞争,锁持有时间长的场景。
- 性能:线程阻塞和唤醒会涉及用户态到内核态的切换,开销大。
- 实现:JVM 会在对象头中写入一个指向
ObjectMonitor的指针。ObjectMonitor是 C++ 实现的,用于管理等待锁的线程队列。
三、锁的动态升级流程
synchronized 锁的升级是一个不可逆的过程。
无锁 → (单线程获取) → 偏向锁 → (出现竞争) → 轻量级锁 → (自旋失败) → 重量级锁
结论:
Java 的锁机制通过动态升级,在不同并发场景下实现了性能和安全性的平衡。理解锁的升级流程,有助于开发者编写高效、线程安全的并发代码。