ReentrantLock 锁优化与 synchronized 锁膨胀的共同点
在 Java 并发编程中,ReentrantLock 和 synchronized 都是用于控制线程同步的锁机制。虽然它们在实现上有所不同,但它们在优化与锁膨胀的过程中具有一些共同点。本文将深入探讨它们的优化机制,并分析它们在高并发场景下的表现。
1. synchronized 锁膨胀机制
synchronized 在 Java 1.6 之后经历了多次优化,主要采用了 偏向锁 -> 轻量级锁 -> 自旋锁 -> 重量级锁 的升级策略。
1.1 锁的升级过程
偏向锁(Biased Locking):
- 适用于单线程访问的情况。
- 线程获取锁时,不会执行 CAS 操作,而是直接记录线程 ID。
- 若没有竞争,则始终由同一线程持有。
轻量级锁(Lightweight Locking):
- 适用于少量线程竞争的情况。
- 线程尝试用 CAS 操作将锁对象的 Mark Word 更新为当前线程 ID。
- 如果 CAS 失败,则表示有竞争,需要升级为自旋锁。
自旋锁(Spin Lock):
- 适用于短时间竞争的情况。
- 线程在获取锁失败时不会立即阻塞,而是执行若干次自旋(默认 10 次)。
- 若在自旋期间仍然获取不到锁,则升级为重量级锁。
重量级锁(Heavyweight Locking):
- 适用于高并发场景。
- 线程竞争激烈时,会触发锁的膨胀,导致线程进入阻塞状态,降低 CPU 空转。
1.2 synchronized 锁膨胀的触发条件
- 线程竞争加剧:多个线程同时竞争锁,导致自旋失败。
- 长时间自旋无效:自旋锁默认执行 10 次,若仍然无法获取锁,则进入重量级锁。
- 对象被多个线程频繁修改:导致偏向锁撤销,退化为轻量级锁或更高级的锁。
2. ReentrantLock 锁优化机制
ReentrantLock 是 java.util.concurrent.locks
包提供的一种显式锁,它通过 AQS(AbstractQueuedSynchronizer)管理线程竞争。与 synchronized 类似,ReentrantLock 也有优化策略,但它提供了更精细的控制能力。
2.1 ReentrantLock 的优化策略
自旋 + CAS:
- 使用 CAS(Compare-And-Swap)操作尝试获取锁,避免线程立即进入阻塞。
- 自旋失败后,进入等待队列,减少 CPU 资源浪费。
公平锁 & 非公平锁:
- 非公平锁(默认):线程尝试直接获取锁,提高吞吐量。
- 公平锁:线程按照队列顺序依次获取锁,减少线程饥饿。
AQS 队列优化:
- 线程在获取锁失败后,进入 AQS 的 FIFO 等待队列。
- 通过
park()
挂起线程,避免频繁自旋消耗 CPU。
可中断锁:
- 线程在等待锁时,可以响应
interrupt()
进行中断,避免死锁问题。
- 线程在等待锁时,可以响应
3. 共同点分析
虽然 synchronized 和 ReentrantLock 采用不同的实现机制,但在锁优化过程中,它们具有以下共同点:
3.1 都采用自旋锁降低线程阻塞成本
- synchronized 在轻量级锁阶段使用 CAS + 自旋,避免线程立即进入阻塞。
- ReentrantLock 采用自旋 + CAS 进行锁竞争,提高性能。
3.2 都提供了从低开销到高开销的锁升级策略
- synchronized 从 偏向锁 -> 轻量级锁 -> 自旋锁 -> 重量级锁 逐步升级。
- ReentrantLock 通过 CAS 获取锁 -> AQS 队列,在竞争激烈时进入等待队列。
3.3 都利用 AQS 或队列管理线程竞争
- synchronized 的重量级锁使用操作系统的 阻塞队列 来管理等待线程。
- ReentrantLock 直接使用 AQS 维护一个 FIFO 队列。
3.4 都可以避免死锁和减少 CPU 资源浪费
- synchronized 通过 自旋锁 + 线程阻塞 机制,降低 CPU 资源浪费。
- ReentrantLock 允许可中断锁,避免线程无限等待,降低死锁风险。
3.5 都支持重入
- synchronized 允许同一线程多次获取同一个锁。
- ReentrantLock 也支持可重入性,即同一线程可以多次 lock() 而不会阻塞自己。
4. 适用场景对比
特性 | synchronized | ReentrantLock |
---|---|---|
实现方式 | JVM 内置锁,依赖对象头 Mark Word | 依赖 AQS,基于 CAS 及队列实现 |
性能优化 | 偏向锁、轻量级锁、自旋、膨胀 | 自旋、AQS 队列、可中断锁 |
自旋锁 | 轻量级锁阶段使用 | 内部优化采用 CAS |
锁升级机制 | 偏向锁 -> 轻量级锁 -> 重量级锁 | CAS -> AQS 排队 |
可重入性 | 支持 | 支持 |
公平性 | 非公平,不能手动控制 | 支持公平锁与非公平锁 |
可中断 | 不支持 | 支持可中断锁 |
适用于 | 简单同步场景 | 复杂并发控制,细粒度锁 |
5. 总结
- synchronized 和 ReentrantLock 都采用了 CAS 和自旋锁优化锁竞争,以减少线程阻塞。
- 两者都提供锁升级机制,以适应不同的竞争强度,提高吞吐量。
- 两者都使用了队列管理线程等待,减少资源浪费,提升性能。
- ReentrantLock 提供了更丰富的功能,如公平锁、可中断锁等,适用于复杂并发场景,而 synchronized 适用于简单同步控制。
在选择时,
- synchronized 适用于锁竞争较小、简单同步的场景。
- ReentrantLock 更适合高并发、大量竞争的复杂业务逻辑。
希望这篇文章能帮助你更好地理解 synchronized 和 ReentrantLock 的优化机制及其共同点。🚀