ReentrantLock 源码深入分析(基于 JDK 8)
一、什么是 ReentrantLock?
ReentrantLock 是 Java 中一种可重入锁(Reentrant Lock),功能上类似于 synchronized,但提供了更高的控制力,例如:
- 可中断锁获取
- 尝试获取锁
- 公平锁和非公平锁
- 手动加锁/释放锁
- 支持条件变量(Condition)
总体设计
public class ReentrantLock implements Lock, java.io.Serializable {
private final Sync sync;
// 公平锁构造器
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
// 默认非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
public void lock() {
sync.lock();
}
public void unlock() {
sync.release(1); // 调用 AQS 的 release
}
}
核心:ReentrantLock 实际上是一个 对 AQS 的封装,真正的锁实现逻辑在内部类 Sync 中。
二、AQS 简介(AbstractQueuedSynchronizer)
核心特性:
- int state:表示同步状态(锁是否持有、重入次数等)
- CLH 队列:用于管理获取失败的线程(FIFO)
- 模板方法模式:子类只需实现
tryAcquire/tryRelease即可
protected final boolean compareAndSetState(int expect, int update);
protected final void setState(int newState);
protected final int getState();
三、非公平锁实现
static final class NonfairSync extends Sync {
final void lock() {
// 尝试直接 CAS 抢锁
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1); // AQS 模板方法
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 尝试获取锁,不判断队列是否有前驱线程(非公平)
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
} else if (current == getExclusiveOwnerThread()) {
// 重入
int nextc = c + acquires;
setState(nextc);
return true;
}
return false;
}
}
解读:
- 非公平锁的策略是:不管队列里有没有线程,先尝试一次 CAS 抢锁再说。
- 成功就直接拿到锁,可能插队,吞吐量高,但会造成“线程饥饿”。
四、公平锁实现
static final class FairSync extends Sync {
final void lock() {
acquire(1);
}
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()) {
// 重入
int nextc = c + acquires;
setState(nextc);
return true;
}
return false;
}
}
解读:
hasQueuedPredecessors():检查队列中是否有前驱线程,如果有就必须排队。- 公平锁避免插队,但带来性能开销。
五、释放锁的逻辑
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); // 支持可重入锁,state为0才真正释放
return free;
}
解读:
- 重入锁通过
state计数:每次lock()增加,unlock()减少 - 当
state == 0时才释放锁 - 锁释放后唤醒队列中的下一个线程(由 AQS 控制)
六、队列等待与唤醒流程(AQS 精髓)
AQS 使用一个 CLH(双向链表)队列 来管理竞争失败的线程:
head <-> Node1 <-> Node2 <-> tail
获取锁失败时:
- 当前线程被封装成 Node 加入队列尾部
- 线程进入 park 状态(阻塞等待)
锁释放时:
- AQS 将 head 的下一个节点唤醒(unpark)
- 唤醒的线程重新尝试获取锁
七、lock() 的完整流程(非公平锁)
Thread A:进入 lock()
-> try CAS(state == 0),成功则 setOwner
-> 否则调用 acquire(1)
-> tryAcquire() 失败
-> 入队 + park(阻塞)
Thread B:unlock()
-> release(1)
-> setState(0),setOwner(null)
-> unpark(queue.next)
八、可视化调用图(简化版)
ReentrantLock.lock()
└──> Sync.lock()
├──> tryAcquire()
└──> acquire(1) → AQS模板 → 阻塞/入队
ReentrantLock.unlock()
└──> Sync.release(1)
└──> tryRelease()
└──> AQS唤醒后继节点
九、总结:ReentrantLock 的底层机制精华
| 机制 | 实现方式 | 说明 |
|---|---|---|
| 重入 | state 计数 + 线程标记 | 支持一个线程重复获取锁 |
| 线程排队 | CLH 双向队列 | 线程获取失败则入队 |
| 加锁 | lock() → acquire(1) → tryAcquire() |
公平或非公平策略 |
| 释放 | unlock() → release(1) → tryRelease() |
唤醒下一个等待线程 |
| 阻塞/唤醒 | LockSupport.park()/unpark() | 线程挂起与唤醒机制 |
十、附加建议 & 常见误区
- 不要忘记 unlock()!:不释放锁会导致其他线程永久阻塞
- 不可跨线程 unlock():否则抛出
IllegalMonitorStateException - 避免死锁:注意多个锁嵌套的顺序,尽量使用
tryLock(timeout)方式替代lock()

浙公网安备 33010602011771号