面试题: Java 中 ReentrantLock 的实现原理是什么?

ReentrantLock 是 Java 提供的一个显式锁实现,位于 java.util.concurrent.locks 包中。

它提供了与 synchronized 类似的同步功能,但相比 synchronized,它更加灵活,并提供了更多的特性,如公平性选择、尝试获取锁、可中断获取锁等。

实现原理

1. 基于 AQS(AbstractQueuedSynchronizer)

ReentrantLock 的核心实现依赖于 AbstractQueuedSynchronizer(简称 AQS),这是一个用于构建锁和其他同步组件的基础框架。AQS 使用了一个 FIFO 队列来管理等待获取锁的线程,通过一个原子变量表示当前锁的状态。

  • 状态管理:在 ReentrantLock 中,AQS 的状态(state)用来表示锁的持有情况。如果 state 为 0,则表示锁未被任何线程持有;如果 state 大于 0,则表示锁已经被某个线程持有,并且数值代表持有该锁的重入次数。
  • 队列机制:当一个线程尝试获取锁失败时,它会被封装成一个节点加入到 AQS 维护的同步队列中等待。只有当它前面的所有节点都已从队列中移除(即获得了锁并释放了锁),该节点对应的线程才能尝试获取锁。

2. 公平锁 vs 非公平锁

ReentrantLock 支持两种类型的锁:公平锁和非公平锁,默认情况下是使用非公平锁。

  • 公平锁:遵循先来先服务的原则,即等待时间最长的线程将最先获得锁。这可以避免“饥饿”现象,但可能会导致性能下降,因为每次都需要检查队列头部是否有其他线程等待。
  • 非公平锁:允许插队,即新来的线程有机会抢占锁,即使已经有其他线程在等待。这种方式通常能提供更好的吞吐量,但在某些情况下可能导致长时间等待的线程始终得不到执行机会。

3. 锁的获取与释放

  • 获取锁
    • 调用 lock() 方法尝试获取锁。如果是非公平锁,会首先尝试CAS操作直接获取锁,成功则立即返回;如果不成功,则进入同步队列等待。
    • 对于公平锁,会直接进入同步队列排队等待,直到轮到自己获取锁。
  • 释放锁
    • 调用 unlock() 方法释放锁。每次调用都会减少锁的重入计数,当计数归零时,锁才真正被释放,并唤醒同步队列中的下一个节点对应的线程。

4. 其他特性

  • 可中断获取锁:通过 lockInterruptibly() 方法可以在获取锁的过程中响应中断请求,这对于需要取消长时间等待的操作非常有用。
  • 尝试获取锁tryLock() 方法允许以非阻塞的方式尝试获取锁,如果此时无法获取锁,则立即返回而不是等待。还可以指定超时时间,如 tryLock(long timeout, TimeUnit unit)
  • 条件变量ReentrantLock 提供了 newCondition() 方法创建条件变量(Condition),使得线程可以在满足特定条件之前暂停执行,类似于传统的监视器对象上的 wait/notify 机制。

综上所述,ReentrantLock 通过 AQS 实现了一套高效且灵活的锁机制,不仅支持基本的同步需求,还提供了多种高级功能,使其成为处理复杂并发场景的强大工具。

THE END
喜欢就支持一下吧
点赞7 分享