ReentrantLock的实现原理
时间: 2025-08-31 16:53:25 AIGC 浏览: 8
<think>
我们正在讨论ReentrantLock的实现原理。根据之前的引用,我们知道ReentrantLock是基于AQS(AbstractQueuedSynchronizer)框架实现的,它提供了比synchronized更灵活的锁操作。ReentrantLock有两种模式:非公平锁(默认)和公平锁,通过构造函数指定。现在,我将详细解释其实现原理。
ReentrantLock的核心实现原理:
1. 基于AQS框架:AQS内部维护了一个同步队列(CLH队列的变体)和一个状态变量(state)。ReentrantLock使用AQS的独占模式。
2. 可重入性:通过记录当前持有锁的线程和重入次数来实现。每次重入,state增加1;释放锁时,state减少1,直到为0时完全释放。
3. 公平锁与非公平锁:两者的区别在于获取锁时是否遵循FIFO(先来先服务)原则。非公平锁在尝试获取锁时直接尝试使用CAS修改state,而公平锁则会先检查队列中是否有等待线程。
具体实现:
- 非公平锁(NonfairSync):在lock()方法中,先尝试直接获取锁(通过CAS设置state),如果成功则设置当前线程为独占线程;否则,调用AQS的acquire(1)方法,该方法会再次尝试获取锁,如果失败则将线程加入队列并阻塞。
- 公平锁(FairSync):在lock()方法中直接调用acquire(1),而在tryAcquire方法中,会先检查队列中是否有等待线程(使用hasQueuedPredecessors()方法),如果没有才尝试获取锁。
下面我们通过代码片段(伪代码)来展示:
```java
// ReentrantLock 中的非公平锁实现
static final class NonfairSync extends Sync {
final void lock() {
if (compareAndSetState(0, 1)) // 直接尝试CAS抢锁
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1); // 调用AQS的acquire方法
}
// ... 其他方法
}
// ReentrantLock 中的公平锁实现
static final class FairSync extends Sync {
final void lock() {
acquire(1); // 直接调用acquire,不尝试抢锁
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 先检查队列中是否有等待线程,没有才尝试获取锁
if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 可重入处理
else if (current == getExclusiveOwnerThread()) {
// ... 增加重入次数
}
return false;
}
}
```
在AQS中,acquire方法如下:
```java
public final void acquire(int arg) {
if (!tryAcquire(arg) && // 尝试获取锁,由子类实现(即上面的FairSync或NonfairSync的tryAcquire)
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 如果失败,则加入队列等待
selfInterrupt();
}
```
关键点:
- 非公平锁:在lock()时直接尝试CAS,可能会插队,所以非公平。
- 公平锁:在tryAcquire()中总是先检查队列(hasQueuedPredecessors),如果有等待的线程,则不会尝试获取锁,从而保证公平。
AQS队列管理:使用一个FIFO队列(双向链表)来管理等待线程。每个节点代表一个等待线程。当线程获取锁失败时,会被包装成节点加入队列尾部,然后阻塞(通过LockSupport.park)。当锁释放时,会唤醒队列的头节点(线程)尝试获取锁。
可重入性:通过记录当前持有锁的线程(exclusiveOwnerThread)和重入次数(state)来实现。每次重入,state递增;释放时递减,直到0才完全释放。
ReentrantLock还支持条件变量(Condition),每个条件变量对应一个等待队列。当调用await()时,线程会释放锁并进入条件队列;当调用signal()时,会将该线程从条件队列转移到锁的同步队列中等待获取锁。
通过上面的分析,我们可以总结ReentrantLock是如何实现可重入、公平/非公平以及条件变量的。
相关问题:
1. 为什么非公平锁性能通常比公平锁高?
2. ReentrantLock的可重入性是如何实现的?
3. AQS框架在ReentrantLock中扮演什么角色?
4. 条件变量(Condition)在ReentrantLock中是如何工作的?
5. 如何避免使用ReentrantLock时可能出现的死锁?
</think>
### ReentrantLock 实现原理详解
ReentrantLock 是 Java 并发包中的**可重入互斥锁**,其核心基于 AQS(AbstractQueuedSynchronizer)框架实现[^2][^3]。以下是其关键原理:
#### 1. **AQS 框架基础**
- **同步状态(`state`)**:
通过 `volatile int state` 表示锁状态:
$$ \text{state} = \begin{cases}
0 & \text{锁空闲} \\
n & \text{锁被持有,且重入 } n \text{ 次}
\end{cases} $$
- **CLH 队列**:
双向链表实现等待队列,存储阻塞线程(公平锁的核心)[^3]。
---
#### 2. **锁获取流程(非公平模式)**
```java
// ReentrantLock.NonfairSync
final void lock() {
if (compareAndSetState(0, 1)) // 直接CAS尝试抢占锁
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1); // 进入AQS队列
}
// AQS核心方法
public final void acquire(int arg) {
if (!tryAcquire(arg) && // 子类实现抢占逻辑
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 加入队列并阻塞
selfInterrupt();
}
```
**关键步骤**:
1. 新线程直接 CAS 修改 `state` 尝试抢占(非公平性来源)
2. 失败后线程加入 CLH 队列尾部
3. 通过 `LockSupport.park()` 阻塞线程[^2]
---
#### 3. **公平锁实现差异**
公平锁在 `tryAcquire()` 中增加队列检查:
```java
// ReentrantLock.FairSync
protected final boolean tryAcquire(int acquires) {
if (!hasQueuedPredecessors()) { // 关键:检查队列是否有等待线程
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(currentThread());
return true;
}
}
return false;
}
```
$$ \text{公平性保证} \iff \text{hasQueuedPredecessors()} \text{ 返回 } false $$
---
#### 4. **可重入性实现**
```java
final boolean nonfairTryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) // 首次获取
setExclusiveOwnerThread(current);
} else if (current == getExclusiveOwnerThread()) {
setState(c + acquires); // 重入:state叠加计数
return true;
}
return false;
}
```
---
#### 5. **锁释放流程**
```java
public void unlock() {
sync.release(1); // 调用AQS释放
}
// AQS释放逻辑
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = (c == 0);
if (free) setExclusiveOwnerThread(null); // 完全释放
setState(c); // 更新state
return free;
}
```
#### 原理对比
| 特性 | 非公平锁 (默认) | 公平锁 |
|--------------------|-----------------------------------|--------------------------------|
| **获取锁顺序** | 允许插队 | 严格 FIFO[^1] |
| **`hasQueuedPredecessors`** | ❌ 不调用 | ✅ 队列检查 |
| **吞吐量** | 更高(减少线程切换)[^2] | 较低(保证公平性) |
---
### 相关问题
1. **为什么非公平锁的 `hasQueuedPredecessors()` 检查会导致性能下降**?该方法在 AQS 中的具体实现逻辑是什么[^3]?
2. 当线程重入锁时,AQS 的 `state` 如何确保**并发修改的安全性**(是否依赖 CAS)[^2]?
3. CLH 队列在**高并发场景下**是否存在性能瓶颈?AQS 如何优化队列操作[^3]?
4. `ReentrantLock` 的 `Condition` 机制如何利用 AQS 实现**精准唤醒**(对比 `synchronized` 的随机唤醒)[^1]?
5. 如何验证公平锁的 FIFO 特性?能否通过代码演示线程按入队顺序获取锁[^1]?
[^1]: 当需要实现公平锁机制时,FairSync实现的ReentrantLock保证线程按请求顺序获取锁
[^2]: ReentrantLock基于AQS框架实现,提供可重入互斥锁功能
[^3]: ReentrantLock基于AQS独占模式实现,区分公平/非公平策略
阅读全文
相关推荐



















