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

ReentrantLock 是 Java 中基于 AQS(AbstractQueuedSynchronizer) 实现的一种可重入独占锁。它提供了比 synchronized 更灵活的锁机制,支持公平锁和非公平锁,并且可以响应中断、设置超时等。


1. ReentrantLock 的核心特性

  • 可重入性
    • 同一个线程可以多次获取同一把锁,而不会造成死锁。
    • 每次获取锁后,锁的持有计数(state)加 1;每次释放锁后,计数减 1。
  • 公平性
    • 公平锁:按照线程请求锁的顺序分配锁。
    • 非公平锁:允许插队,新请求的线程可以直接尝试获取锁。
  • 支持中断
    • 线程在等待锁的过程中可以响应中断。
  • 支持超时
    • 线程可以尝试在指定时间内获取锁,超时后放弃。

2. ReentrantLock 的实现原理

  • 基于 AQS
    • ReentrantLock 的核心功能是通过 AQS 实现的。
    • AQS 提供了一个 state 变量来表示锁的状态,以及一个 CLH 队列来管理等待锁的线程。
  • 独占模式
    • ReentrantLock 使用 AQS 的独占模式,同一时刻只有一个线程可以持有锁。
  • 可重入性
    • 通过 state 变量记录锁的重入次数。每次获取锁时,state 加 1;每次释放锁时,state 减 1。

3. ReentrantLock 的源码分析

  • Sync 内部类
    • ReentrantLock 的核心功能是通过 Sync 内部类实现的,Sync 继承自 AQS。
    • Sync 有两个子类:NonfairSync(非公平锁)和 FairSync(公平锁)。
  • 非公平锁的实现
    • 新请求的线程可以直接尝试获取锁,如果失败则加入队列。
    • 源码片段
      final void lock() {
          if (compareAndSetState(0, 1)) // CAS 尝试获取锁
              setExclusiveOwnerThread(Thread.currentThread()); // 设置当前线程为锁持有者
          else
              acquire(1); // 调用 AQS 的 acquire 方法
      }
  • 公平锁的实现
    • 新请求的线程必须加入队列,按照 FIFO 顺序获取锁。
    • 源码片段
      final void lock() {
          acquire(1); // 直接调用 AQS 的 acquire 方法
      }

4. ReentrantLock 的使用示例

  • 基本用法
    ReentrantLock lock = new ReentrantLock();
    lock.lock(); // 获取锁
    try {
        // 临界区代码
    } finally {
        lock.unlock(); // 释放锁
    }
  • 支持中断
    ReentrantLock lock = new ReentrantLock();
    try {
        lock.lockInterruptibly(); // 可中断地获取锁
        // 临界区代码
    } catch (InterruptedException e) {
        // 处理中断
    } finally {
        if (lock.isHeldByCurrentThread()) {
            lock.unlock(); // 释放锁
        }
    }
  • 支持超时
    ReentrantLock lock = new ReentrantLock();
    try {
        if (lock.tryLock(1, TimeUnit.SECONDS)) { // 尝试在 1 秒内获取锁
            // 临界区代码
        } else {
            // 超时处理
        }
    } catch (InterruptedException e) {
        // 处理中断
    } finally {
        if (lock.isHeldByCurrentThread()) {
            lock.unlock(); // 释放锁
        }
    }

5. ReentrantLock 与 synchronized 的对比

特性ReentrantLocksynchronized
实现方式基于 AQS,Java 代码实现JVM 内置实现
可重入性支持支持
公平性支持公平锁和非公平锁仅支持非公平锁
支持中断支持不支持
支持超时支持不支持
性能高并发下性能更好低并发下性能更好
锁绑定条件支持多个条件变量(Condition不支持

6. ReentrantLock 的优缺点

  • 优点
    • 提供了比 synchronized 更灵活的锁机制。
    • 支持公平锁、可中断、超时等特性。
    • 在高并发场景下性能优异。
  • 缺点
    • 使用复杂,需要手动加锁和释放锁。
    • 容易忘记释放锁,导致死锁。

7. 总结

  • ReentrantLock 是基于 AQS 实现的可重入独占锁,支持公平锁和非公平锁。
  • 它提供了比 synchronized 更灵活的锁机制,支持中断、超时等特性。
  • 在高并发场景下,ReentrantLock 的性能通常优于 synchronized
  • 使用 ReentrantLock 时需要注意手动加锁和释放锁,避免死锁问题。

8. 扩展:Condition 的使用

  • ReentrantLock 支持多个条件变量(Condition),用于实现线程间的精确唤醒。
  • 示例代码
    ReentrantLock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    
    lock.lock();
    try {
        condition.await(); // 当前线程等待
        condition.signal(); // 唤醒一个等待线程
    } finally {
        lock.unlock();
    }
THE END
点赞10 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容