ReentrantLock 如何实现可重入的?
1 个回答
时间不在乎你拥有多少,而在于你怎样使用。小王子-B644**`ReentrantLock` 实现可重入的原理**主要基于以下几个核心点: ### 1. **可重入的定义** 可重入锁允许同一线程多次获取同一把锁,而不会导致死锁。例如,一个线程进入一个同步方法后,可以继续调用其他受相同锁保护的方法,而不需要再次等待。 ### 2. **`ReentrantLock` 的实现细节** `ReentrantLock` 是基于 **AQS(AbstractQueuedSynchronizer)** 实现的。它通过以下机制实现可重入性: #### 2.1 **加锁逻辑** 当一个线程尝试获取锁时,`ReentrantLock` 会检查当前持有锁的线程是否与请求锁的线程相同: - 如果是同一线程,则允许重入。 - 每次重入时,通过增加一个计数器(`state`)来记录锁的重入次数。 对应代码: ```java if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } ``` - **`state` 变量**: - `state` 是一个整数,用于表示锁的状态。 - 当 `state = 0` 表示锁未被任何线程持有。 - 当 `state > 0` 表示锁已被某个线程持有,并记录了重入的次数。 - **线程持有者**: - `getExclusiveOwnerThread()` 返回当前持有锁的线程。 - `setExclusiveOwnerThread(thread)` 设置当前锁的持有线程。 #### 2.2 **释放锁逻辑** 在释放锁时,需要确保锁的重入次数全部释放后,才能真正释放锁资源: - 每次调用 `unlock`,`state` 的值会递减。 - 只有当 `state` 减为 0 时,锁才会完全释放,并清空持有线程。 对应代码: ```java protected final boolean tryRelease(int releases) { int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { free = true; setExclusiveOwnerThread(null); } setState(c); return free; } ``` - **检查线程**: - 如果当前线程不是锁的持有线程,直接抛出异常,防止其他线程释放锁。 - **清空持有线程**: - 当 `state = 0` 时,调用 `setExclusiveOwnerThread(null)`,清除锁的持有线程信息。 ### 3. **重入的完整流程** 1. **加锁**: - 第一次获取锁:`state = 1`,记录当前线程为持有线程。 - 重入时:`state` 递增,例如第二次重入时 `state = 2`。 2. **解锁**: - 每次解锁:`state` 递减。 - 当 `state = 0` 时,完全释放锁,并清除锁的持有线程信息。 ### 4. **与 `synchronized` 的区别** - **`synchronized`**: - 是 JVM 层面的内置锁,重入性由 JVM 实现,线程的进入和退出由字节码指令 `monitorenter` 和 `monitorexit` 自动处理。 - 不需要显式维护状态,性能和灵活性有限。 - **`ReentrantLock`**: - 是 Java 层面实现的锁,依赖 `AQS` 提供的框架,具有更高的可扩展性和灵活性。 - 支持公平锁、非公平锁,以及可中断和超时的获取锁机制。 ### 5. **总结** `ReentrantLock` 通过 `state` 计数器和持有线程记录来实现可重入性: 1. 锁被当前线程持有时,允许重入,并增加 `state` 计数。 2. 每次释放锁时,`state` 减少,直到 `state = 0` 时完全释放。 3. 使用 `AQS` 提供的线程同步工具,确保多线程环境下操作的原子性和安全性。发布于 2025-01-22 16:52 •IP属地 黑龙江举报评论收藏
公众号进入网站体验更佳

