Java锁机制:从偏向锁到重量级锁的动态升级

276 阅读2分钟

Java锁机制:从偏向锁到重量级锁的动态升级


一、synchronized锁的底层实现:对象头与Mark Word

synchronized是 Java 内置的锁机制,其底层实现是基于**Java对象的对象头(Object Header)**中的 Mark WordMark 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 的锁机制通过动态升级,在不同并发场景下实现了性能和安全性的平衡。理解锁的升级流程,有助于开发者编写高效、线程安全的并发代码。