【非公平可重入锁 RentrantLock#lock() 与 unlock() 的执行链路源码解析】
state含义:在 ReentrantLock 中表示当前持有锁的重入计数,arg通常是1。快速路径 vs 慢路径:非公平锁优先用 CAS 快速获得锁;若失败则入队(可能又有快速 tail 插入)。队列保证公平性只有在时才调用tryAcquire,从而保证队列 FIFO 的“唤醒顺序”。park/unpark 协议:通过waitStatus的SIGNAL标志和的状态转换,防止“挂起后无人唤醒”或“
·
下面我把 JDK 8 中 new ReentrantLock()(非公平)对象上调用 lock.lock() 与 lock.unlock() 时,在运行链路上会依次触发的相关方法源码按执行顺序列出来(用简化但几乎与 OpenJDK 8 等价的代码段),并在每段代码后紧跟逐步讲解。目标是让你能按“调用栈顺序”看到 AQS + ReentrantLock 的整个工作流,并理解每一步为什么要这样做。
说明:
- 我只列出「在典型路径上」会被执行的核心方法(即非公平锁、第一次获取时 CAS 失败导致入队的路径,以及释放时唤醒后继的路径)。
- 为了阅读性,我把部分辅助函数(如 CAS 原语)以注释形式说明,但保留源码最关键逻辑。
- 所有代码片段基于 OpenJDK8 实现风格(已简化注释和异常处理以便理解),并在每段后用通俗语言解释。
A. lock() 的总体入口与主要分支
1) java.util.concurrent.locks.ReentrantLock#lock()
public void lock() {
sync.lock();
}
说明:ReentrantLock 对外的 lock() 只是一个简单代理,把请求交给内部 Sync(NonfairSync 或 FairSync)来处理。对于 new ReentrantLock() 默认是 NonfairSync(非公平)。
2) ReentrantLock.NonfairSync#lock()(非公平实现)
static final class NonfairSync extends Sync {
final void lock() {
// 快速路径:试图用 CAS 直接设置 state=1,成为持有者
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
// 慢路径:尝试通过 AQS.acquire(1) 去入队/等待
acquire(1);
}
}
解释:
- 非公平锁尝试无队列直接抢占(
compareAndSetState(0,1))。如果成功,就拿到锁,不需要排队。 - 如果失败(例如其它线程已持有锁或多线程并发),走 AQS 的
acquire(1)模板方法(进入排队/阻塞流程)。
B. acquire(1) 的调用链(入队与等待)
3) AbstractQueuedSynchronizer#acquire(int arg)
public final void acquire(int arg) {
// 先尝试子类的 tryAcquire(arg)(子类 ReentrantLock.Sync 实现)
if (!tryAcquire(arg) &&
// 如果尝试失败,就把当前线程入队并在队列中等待获取(acquireQueued)
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
// 若等待期间被中断,置中断状态
selfInterrupt();
}
解释:
acquire是 AQS 的模板:先快速尝试(子类实现),失败则把线程包装成Node入队(addWaiter),再在队列中循环等待(acquireQueued)。arg在 ReentrantLock 中就是1(每次占用一个“许可”——即一次锁定)。
4) ReentrantLock.Sync#tryAcquire(int arg)(Sync 是 ReentrantLock 的 AQS 子类)
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 没有线程占用,尝试拿下(公平模式会检查队列,但 Sync 的非公平实现由 NonfairSync.lock 先用 CAS)
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
} else if (current == getExclusiveOwnerThread()) {
// 可重入:当前线程已经是 owner,state 累加
int nextc = c + acquires;
setState(nextc);
return true;
}
return false;
}
解释:
state表示锁的持有计数(重入次数)。- 若
state == 0,试图把state设置为acquires(1),并把当前线程设为 owner。 - 若当前线程已经是 owner,则允许重入(
state += acquires)。 - 否则返回
false表示不能立即获得锁,进入队列等待。
5) AbstractQueuedSynchronizer#addWaiter(int mode)(把当前线程封装成 Node 并尝试尾插 - 快速路径)
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// 快速尾插尝试
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 快速路径失败走慢路径
return enq(node);
}
解释:
- 新建代表当前线程的
Node。 - 先尝试用 CAS 快速把 node 插入队尾(大多数情况下成功)。
- 快速路径失败(或队列未初始化)则调用
enq(node)(慢路径,自旋直到入队成功)。
6) AbstractQueuedSynchronizer#enq(Node node)(慢路径,保证入队)
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // 如果队列尚未初始化
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) { // CAS 成功则把 t.next 指向 node
t.next = node;
return node;
}
}
}
}
解释:
- 自旋直到插入成功。若队列尚未建立,会先创建虚拟
head,并把tail = head,然后继续尾插。 tail是队尾权威指针,用 CAS 设尾,随后设置t.next连接链表。
7) AbstractQueuedSynchronizer#acquireQueued(Node node, int arg)(等待循环)
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
解释(重点):
- 循环体:在队列中轮询自己的前驱
p。 - 仅当
p == head(即自己是队首)时才试tryAcquire(arg),以保证 FIFO 顺序(队首优先)。 shouldParkAfterFailedAcquire用于决定并协商是否可以安全地挂起(park)当前线程(详见后文)。parkAndCheckInterrupt真正执行LockSupport.park并返回是否被中断。- 成功获得锁后通过
setHead(node)把自己设为head(并清理thread引用),返回是否曾被中断过。 - 若异常/退出且未成功,会调用
cancelAcquire(node)清理队列中的节点。
8) shouldParkAfterFailedAcquire(Node pred, Node node)
简化逻辑:
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
return true; // 已经被标记为 SIGNAL,说明 pred 在释放时会唤醒后继
if (ws > 0) {
// 前驱已取消,跳过被取消节点
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
// 尝试把 pred.waitStatus 设为 SIGNAL,告知 pred 在释放时唤醒后继
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
解释:
- 保证在 park 之前,前驱是处于
SIGNAL(会唤醒后继)的状态;如前驱是 CANCELLED,则跳过;若为 0,则尝试设置为 SIGNAL 后再下一轮 park。
9) parkAndCheckInterrupt()
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
解释:
- 使用
LockSupport.park将线程挂起,直到被unpark或被中断。 Thread.interrupted()返回并清除中断状态,acquireQueued根据结果记录是否发生过中断(但对于不可中断的acquire只是记录并继续等待,最后返回让上层决定是否恢复中断)。
10) AbstractQueuedSynchronizer#setHead(Node node)
private void setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
}
解释:
- 把当前已获取同步状态的节点设为 head,清理 thread、prev 避免内存泄漏;head 表示“队列的头(正在持有或刚获得资源的节点)”。
C. unlock() 的调用链(释放锁与唤醒后继)
11) ReentrantLock#unlock()
public void unlock() {
sync.release(1);
}
解释:调用 AQS 的 release(1) 来释放一次持有计数(arg = 1),若 state 减到 0,表示当前线程完全释放锁,AQS 会唤醒队列中的下一个等待者。
12) AbstractQueuedSynchronizer#release(int arg)(模板方法)
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
解释:
tryRelease(arg)是由子类(ReentrantLock.Sync)实现,用于真正地改变state并在必要时返回true表示“完全释放”。- 若完全释放且队列头存在且 head.waitStatus != 0,则唤醒后继(
unparkSuccessor(head)),这样队首线程会被唤醒去尝试获取锁。
13) ReentrantLock.Sync#tryRelease(int releases)(子类实现)
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;
}
解释:
- 检查当前线程是否是 owner,否则抛异常。
- 将
state减releases(1)。若减到 0,表示锁完全释放,把 owner 清空并返回true,AQS 的release将基于此唤醒后继。
14) AbstractQueuedSynchronizer#unparkSuccessor(Node node)
关键逻辑(简化):
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0); // 清理 head 的 waitStatus
Node s = node.next;
if (s == null || s.waitStatus > 0) {
// 从 tail 向前找到未取消的候选
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}
解释:
- 找到
node(通常是 head)的后继s(如果node.next被取消或为空,则从 tail 向前查找最近的非取消节点),然后对该节点的线程调用LockSupport.unpark唤醒它。 unpark将让等待在LockSupport.park的线程返回,从而在acquireQueued循环中再次尝试tryAcquire。
D. 补充:常用的辅助函数(涉及并发语义但未全文列出)
compareAndSetState(int expect, int update):底层Unsafe.compareAndSwapInt,用于 CAS 修改state。compareAndSetTail(Node expect, Node update)、compareAndSetHead(Node update):CAS 修改tail/head。cancelAcquire(node):当线程在等待中被中断或超时、或方法出现异常,需要把节点从队列中清理的复杂逻辑(包括将该节点标记CANCELLED并修剪链表)。
E. 以一个典型执行场景串在一起(文字版执行顺序回顾)
当线程 T 调 lock.lock()(非公平)时:
ReentrantLock.lock()→NonfairSync.lock()。- 尝试
compareAndSetState(0,1):如果成功,setExclusiveOwnerThread(T)并返回(拿到锁)。 - 如果 CAS 失败,进入
acquire(1)。 acquire(1)内先调用tryAcquire(1)(Sync.tryAcquire),失败则addWaiter(Node.EXCLUSIVE)。addWaiter快速路径 CAS 插入 tail(或enq(node)慢路径自旋),返回新节点。acquireQueued(node, 1):循环检查node.predecessor()是否为head且tryAcquire(1)成功;否则使用shouldParkAfterFailedAcquire协商并park(阻塞)。- 当前持有锁的线程调用
unlock()时:sync.release(1)→tryRelease(1)更新 state,并在彻底释放(state==0)时调用unparkSuccessor(head)。 unparkSuccessor找到队首后继并LockSupport.unpark(thread)唤醒它。- 被唤醒的线程在
parkAndCheckInterrupt()返回,循环再次检查p == head && tryAcquire(1),若成功则setHead(node)并退出,成为新的持有者。
F. 关键点与易错理解(总结要点)
state含义:在 ReentrantLock 中表示当前持有锁的重入计数,arg通常是1。- 快速路径 vs 慢路径:非公平锁优先用 CAS 快速获得锁;若失败则入队(可能又有快速 tail 插入)。
- 队列保证公平性:
acquireQueued只有在predecessor == head时才调用tryAcquire,从而保证队列 FIFO 的“唤醒顺序”。 - park/unpark 协议:通过
waitStatus的SIGNAL标志和shouldParkAfterFailedAcquire的状态转换,防止“挂起后无人唤醒”或“唤醒丢失”的 race。 - 中断处理:不可中断的
acquire在等待期间若被中断仅记录,最终获得锁后再恢复中断;可中断版本会在中断时抛出InterruptedException并清理队列。
更多推荐


所有评论(0)