目录
[1. 非公平锁实现原理](#1. 非公平锁实现原理)
ReentrantLock原理

1. 非公平锁实现原理
加锁成功流程
先从构造器开始看,默认为非公平锁实现
java
public ReentrantLock() {
sync = new NonfairSync();
}
NonfairSync 继承自 AQS
没有竞争时
如果 CAS 成功,说明当前线程抢占锁成功,设置当前线程为独占锁的拥有者

java
// 非公平锁的实现类,继承自 Sync(Sync 是 ReentrantLock 内部的抽象同步器)
static final class NonfairSync extends Sync {
// 序列化版本号,用于 Java 序列化机制
private static final long serialVersionUID = 7316153563782823691L;
// 注释说明:执行加锁操作。先尝试直接抢占锁(不排队),抢占失败后回退到正常的锁获取流程
final void lock() {
// CAS 操作:尝试将同步状态(state)从 0(未锁定)更新为 1(已锁定)
// state 是 AQS(AbstractQueuedSynchronizer)中的核心变量,用于表示锁的状态
if (compareAndSetState(0, 1))
// 如果 CAS 成功,说明当前线程抢占锁成功,设置当前线程为独占锁的拥有者
setExclusiveOwnerThread(Thread.currentThread());
else
// 如果 CAS 失败,调用 AQS 的 acquire 方法,进入正常的锁获取流程(排队、自旋等)
// 参数 1 表示当前线程尝试获取 1 把锁(可重入锁的计数逻辑)
acquire(1);
}
// 尝试获取锁的具体实现,重写 AQS 的 tryAcquire 方法
// acquires 参数表示当前线程尝试获取的锁数量(对于 ReentrantLock 是 1,支持可重入时会累加)
protected final boolean tryAcquire(int acquires) {
// 调用非公平锁的 tryAcquire 实现,核心逻辑在 nonfairTryAcquire 中
return nonfairTryAcquire(acquires);
}
}
加锁失败流程
第一个竞争出现时
- CAS 尝试将 state 由 0 改为 1,结果失败

- 进入 tryAcquire 逻辑,这时 state 已经是1,结果仍然失败的
java
public final void acquire(int arg) {
// 1. 尝试获取锁:调用 tryAcquire 方法(由子类实现,比如 ReentrantLock 的 Sync 类)
// 如果 tryAcquire 返回 true,说明获取锁成功,直接返回,不进入后续逻辑
// 如果返回 false,说明获取锁失败,进入排队流程
if (!tryAcquire(arg) &&
// 2. 获取锁失败后,执行的逻辑:
// a. addWaiter(Node.EXCLUSIVE):将当前线程封装成 Node,加入等待队列尾部
// b. acquireQueued(...):让 Node 在队列中自旋/挂起,等待获取锁的机会
// c. 整个 acquireQueued(...) 返回 true 表示当前线程在等待过程中被中断
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
// 3. 如果 acquireQueued 返回 true(即线程在等待中被中断),执行自我中断
selfInterrupt();
}
- 接下来进入 addWaiter 逻辑,构造 Node 队列

图中黄色三角表示该 Node 的 waitStatus 状态,其中 0 为默认正常状态
Node 的创建是懒惰的
其中第一个 Node 称为 Dummy(哑元)或哨兵,用来占位,并不关联线程
当前线程进入 acquireQueued 逻辑

java
// 方法作用:让节点在 AQS 队列中自旋/挂起,直到获取锁或被中断
// node:当前线程对应的等待队列节点
// arg:获取锁的参数(ReentrantLock 中通常为 1,代表获取 1 把锁)
final boolean acquireQueued(final Node node, int arg) {
// 标记是否获取锁失败(用于 finally 中取消节点)
boolean failed = true;
try {
// 标记线程是否在等待过程中被中断
boolean interrupted = false;
// 自旋:不断尝试获取锁,直到成功或被中断
for (;;) {
// 1. 获取当前节点的前驱节点
final Node p = node.predecessor();
// 2. 如果前驱是头节点,尝试获取锁
if (p == head && tryAcquire(arg)) {
// 2.1 成功获取锁:将当前节点设为新的头节点
setHead(node);
// 2.2 断开前驱节点的 next 引用,帮助 GC 回收
p.next = null;
// 2.3 标记获取锁成功,后续不再执行 cancelAcquire
failed = false;
// 2.4 返回中断状态(false 表示未被中断)
return interrupted;
}
// 3. 获取锁失败:判断是否需要挂起当前线程
if (shouldParkAfterFailedAcquire(p, node) &&
// 3.1 挂起线程,并检查是否被中断
parkAndCheckInterrupt())
// 3.2 如果被中断,标记 interrupted 为 true
interrupted = true;
}
} finally {
// 4. 如果获取锁失败(failed 仍为 true),取消当前节点在队列中的等待
if (failed)
cancelAcquire(node);
}
}
-
acquireQueued 会在一个死循环中不断尝试获得锁,失败后进入 park 阻塞
-
如果自己是紧邻着 head(排第二位),那么再次 tryAcquire 尝试获取锁,当然这时 state 仍为 1,失败

-
进入 shouldParkAfterFailedAcquire 逻辑,将前驱 node,即 head 的 waitStatus 改为 -1(有职责去唤醒它的后继结点),这次返回 false
-
shouldParkAfterFailedAcquire 执行完毕回到 acquireQueued ,再次 tryAcquire 尝试获取锁,当然这时 state 仍为 1,失败
-
当再次进入 shouldParkAfterFailedAcquire 时,这时因为其前驱 node 的 waitStatus 已经是 -1,这次返回 true
-
进入 parkAndCheckInterrupt, Thread-1 park(灰色表示)
java
// parkAndCheckInterrupt 方法:挂起当前线程,并检查线程是否被中断
private final boolean parkAndCheckInterrupt() {
// 1. 挂起当前线程
// LockSupport.park(this):将当前线程挂起,直到被 unpark 或被中断
// 参数 this(Node 实例)是" blocker",用于线程 Dump 时显示更友好的调试信息(关联到 AQS 队列节点)
LockSupport.park(this);
// 2. 线程被唤醒后,检查当前线程的中断状态
// Thread.interrupted():返回当前线程的中断状态,并清除中断标记
return Thread.interrupted();
}

再次有多个线程经历上述过程竞争失败,变成这个样子

Thread-0 释放锁,进入 tryRelease 流程
java
// tryRelease 方法:尝试释放锁
// releases:释放的锁数量(ReentrantLock 中通常为 1,代表释放 1 把锁)
protected final boolean tryRelease(int releases) {
// 1. 计算释放后的 state 值
// getState():获取 AQS 中表示锁状态的变量(state)
// c = 原 state 值 - 释放的数量(releases)
int c = getState() - releases;
// 2. 检查当前线程是否是锁的持有者
// getExclusiveOwnerThread():获取当前持有锁的线程
// 如果当前线程不是持有者,抛出异常(保证锁的释放逻辑正确)
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
// 3. 标记锁是否完全释放(state == 0)
boolean free = false;
// 如果释放后 state == 0,说明锁完全释放
if (c == 0) {
free = true;
// 清除锁的持有者(标记为 null)
setExclusiveOwnerThread(null);
}
// 4. 更新 state 的值(可能是部分释放,比如可重入锁的重入次数减 1)
setState(c);
// 5. 返回是否完全释放锁(free 为 true 表示完全释放)
return free;
}
解锁竞争成功流程
设置 exclusiveOwnerThread 为 null
state = 0

当前队列不为 null,并且 head 的 waitStatus = -1,进入 unparkSuccessor 流程
java
// release 方法:释放锁的核心逻辑
public final boolean release(int arg) {
// 1. 尝试释放锁(由子类实现,比如 ReentrantLock 的 tryRelease)
if (tryRelease(arg)) {
// 2. 如果释放成功,获取队列的头节点
Node h = head;
// 3. 检查头节点是否有效(非空)且头节点的等待状态不是 0
// waitStatus == 0:初始状态;>0:已取消;<0:需要唤醒(如 SIGNAL)
if (h != null && h.waitStatus != 0)
// 4. 唤醒头节点的后继节点(让后继线程尝试获取锁)
unparkSuccessor(h);
return true;
}
// 如果释放锁失败(tryRelease 返回 false),返回 false
return false;
}
java
private void unparkSuccessor(Node node) {
// 注释说明:如果节点状态为负数(可能需要唤醒),尝试清除状态(提前置为 0)
// 即使清除失败或状态被其他线程修改,也不影响后续逻辑
int ws = node.waitStatus;
if (ws < 0)
// CAS 操作:将节点的 waitStatus 从 ws 改为 0
// 作用:清理节点状态,避免重复唤醒
compareAndSetWaitStatus(node, ws, 0);
// 注释说明:需要唤醒的线程保存在后继节点中,通常是直接取 node.next
// 但如果后继节点为 null 或已取消(waitStatus > 0),则从队列尾部向前遍历,找到第一个未取消的后继节点
Node s = node.next;
// 如果后继节点为 null,或后继节点的 waitStatus > 0(已取消)
if (s == null || s.waitStatus > 0) {
s = null;
// 从队列尾部(tail)向前遍历,找到最前面的未取消节点(waitStatus <= 0)
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
// 如果找到有效的后继节点,唤醒它
if (s != null)
LockSupport.unpark(s.thread);
}
找到队列中离 head 最近的一个 Node(没取消的),unpark 恢复其运行,本例中即为 Thread-1
回到 Thread-1 的 acquireQueued 流程
java
// 方法作用:让节点在 AQS 队列中自旋/挂起,直到获取锁或被中断
// node:当前线程对应的等待队列节点
// arg:获取锁的参数(ReentrantLock 中通常为 1,代表获取 1 把锁)
final boolean acquireQueued(final Node node, int arg) {
// 标记是否获取锁失败(用于 finally 中取消节点)
boolean failed = true;
try {
// 标记线程是否在等待过程中被中断
boolean interrupted = false;
// 自旋:不断尝试获取锁,直到成功或被中断
for (;;) {
// 1. 获取当前节点的前驱节点
final Node p = node.predecessor();
// 2. 如果前驱是头节点,尝试获取锁
if (p == head && tryAcquire(arg)) {
// 2.1 成功获取锁:将当前节点设为新的头节点
setHead(node);
// 2.2 断开前驱节点的 next 引用,帮助 GC 回收
p.next = null;
// 2.3 标记获取锁成功,后续不再执行 cancelAcquire
failed = false;
// 2.4 返回中断状态(false 表示未被中断)
return interrupted;
}
// 3. 获取锁失败:判断是否需要挂起当前线程
if (shouldParkAfterFailedAcquire(p, node) &&
// 3.1 挂起线程,并检查是否被中断
parkAndCheckInterrupt())
// 3.2 如果被中断,标记 interrupted 为 true
interrupted = true;
}
} finally {
// 4. 如果获取锁失败(failed 仍为 true),取消当前节点在队列中的等待
if (failed)
cancelAcquire(node);
}
}

解锁竞争失败流程
如果加锁成功(没有竞争),会设置
exclusiveOwnerThread 为 Thread-1,state = 1
head 指向刚刚 Thread-1 所在的 Node,该 Node 清空 Thread
原本的 head 因为从链表断开,而可被垃圾回收
如果这时候有其它线程来竞争(非公平的体现),例如这时有 Thread-4 来了

如果不巧又被 Thread-4 占了先
Thread-4 被设置为 exclusiveOwnerThread,state = 1
Thread-1 再次进入 acquireQueued 流程,获取锁失败,重新进入 park 阻塞
可重入原理
当同一线程调用两次nonfairTryAcquire
java
static final class NonfairSync extends Sync {
// ...(其他代码)
// 非公平锁的 tryAcquire 实现(Sync 继承自 AQS)
// acquires:获取锁的次数(ReentrantLock 中通常为 1)
final boolean nonfairTryAcquire(int acquires) {
// 1. 获取当前线程
final Thread current = Thread.currentThread();
// 2. 获取 AQS 的 state(表示锁的状态,0 为未占用)
int c = getState();
// 3. 如果 state == 0(锁未被占用)
if (c == 0) {
// 3.1 尝试用 CAS 抢占锁(非公平性体现:不管队列,直接抢占)
if (compareAndSetState(0, acquires)) {
// 3.2 抢占成功:标记当前线程为锁的持有者
setExclusiveOwnerThread(current);
return true;
}
}
// 4. 如果 state != 0(锁已被占用),检查是否是当前线程持有(可重入逻辑)
else if (current == getExclusiveOwnerThread()) {
// 4.1 计算重入后的 state(state += acquires)
int nextc = c + acquires;
// 4.2 防止溢出(理论上不会发生,除非恶意调用)
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
// 4.3 更新 state(可重入次数 +1)
setState(nextc);
return true;
}
// 5. 抢占失败且不可重入,返回 false
return false;
}
// 释放锁的实现(Sync 继承自 AQS)
// releases:释放锁的次数(通常为 1)
protected final boolean tryRelease(int releases) {
// 1. 计算释放后的 state(state -= releases)
int c = getState() - releases;
// 2. 检查当前线程是否是锁的持有者(防止非法释放)
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
// 3. 标记是否完全释放锁(state == 0)
boolean free = false;
if (c == 0) {
free = true;
// 3.1 完全释放:清除锁的持有者
setExclusiveOwnerThread(null);
}
// 4. 更新 state(可能是部分释放,如可重入次数减 1)
setState(c);
// 5. 返回是否完全释放锁
return free;
}
}
可打断模式
不可打断模式
线程在 acquireQueued
中自旋尝试获取锁时,若被中断,不会立即退出队列,而是继续自旋竞争锁。中断标记会被临时清除,仅在成功获取锁后 ,才通过 acquire
方法的 selfInterrupt
重新触发中断,保证业务逻辑能感知到中断事件,同时不影响锁竞争流程。
java
// Sync 继承自 AQS(AbstractQueuedSynchronizer),作为同步器基础
static final class NonfairSync extends Sync {
// ...(其他已有逻辑)
// parkAndCheckInterrupt:挂起线程并检查中断状态
private final boolean parkAndCheckInterrupt() {
// LockSupport.park(this):挂起当前线程,需通过 unpark 或中断唤醒
// 参数 this(Node 实例)用于调试,线程 Dump 时显示关联的 AQS 队列节点
LockSupport.park(this);
// Thread.interrupted():返回当前线程中断状态,并清除中断标记
return Thread.interrupted();
}
// acquireQueued:在 AQS 队列中自旋尝试获取锁,处理中断逻辑(不可打断模式核心)
final boolean acquireQueued(final Node node, int arg) {
// failed:标记锁获取是否失败,失败时需取消节点等待
boolean failed = true;
try {
// interrupted:记录线程在等待过程中是否被中断
boolean interrupted = false;
// 自旋循环,持续尝试获取锁
for (;;) {
// 获取当前节点的前驱节点
final Node p = node.predecessor();
// 若前驱是头节点,尝试获取锁(tryAcquire 为 AQS 抽象方法,由子类实现)
if (p == head && tryAcquire(arg)) {
// 成功获取锁,将当前节点设为新头节点
setHead(node);
// 断开前驱节点引用,协助 GC
p.next = null;
// 标记获取锁成功,后续 finally 不执行 cancelAcquire
failed = false;
// 返回中断状态(需获取锁后才反馈中断)
return interrupted;
}
// 若获取锁失败,判断是否需要挂起线程,并处理中断
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt()) {
// 若因中断唤醒,标记 interrupted 为 true
interrupted = true;
}
}
} finally {
// 若获取锁失败(failed 仍为 true),取消当前节点的等待状态
if (failed) {
cancelAcquire(node);
}
}
}
// acquire:AQS 对外的获取锁入口方法
public final void acquire(int arg) {
// 尝试获取锁,若失败则入队并进入 acquireQueued 自旋
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) {
// 若 acquireQueued 返回 true(线程在等待中被中断),补充中断标记
selfInterrupt();
}
}
// selfInterrupt:重新触发当前线程中断(因 acquireQueued 清除了中断标记)
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
}
可打断模式
java
static final class NonfairSync extends Sync {
// 可中断的锁获取方法
// arg:获取锁的参数(通常为 1)
public final void acquireInterruptibly(int arg) throws InterruptedException {
// 检查当前线程是否已中断
if (Thread.interrupted())
// 若已中断,抛出 InterruptedException
throw new InterruptedException();
// 尝试获取锁
if (!tryAcquire(arg))
// 若获取失败,进入可中断的队列获取逻辑
doAcquireInterruptibly(arg);
}
// 可中断的队列获取锁逻辑
// arg:获取锁的参数(通常为 1)
private void doAcquireInterruptibly(int arg) throws InterruptedException {
// 将当前线程封装为 Node,加入 AQS 等待队列
final Node node = addWaiter(Node.EXCLUSIVE);
// 标记是否获取锁失败
boolean failed = true;
try {
// 自旋尝试获取锁
for (;;) {
// 获取当前节点的前驱节点
final Node p = node.predecessor();
// 如果前驱是头节点,尝试获取锁
if (p == head && tryAcquire(arg)) {
// 成功获取锁,将当前节点设为新的头节点
setHead(node);
// 断开前驱节点的 next 引用,帮助 GC 回收
p.next = null;
// 标记获取锁成功
failed = false;
// 退出方法
return;
}
// 如果获取锁失败,判断是否需要挂起线程
if (shouldParkAfterFailedAcquire(p, node) &&
// 挂起线程,并检查是否被中断
parkAndCheckInterrupt()) {
// 若被中断,抛出 InterruptedException
throw new InterruptedException();
}
}
} finally {
// 如果获取锁失败(failed 为 true),取消当前节点的等待
if (failed)
cancelAcquire(node);
}
}
}
公平锁实现原理
java
static final class FairSync extends Sync {
// 序列化版本号
private static final long serialVersionUID = -3000897897090466540L;
// 公平锁的获取锁方法
final void lock() {
acquire(1);
}
// AQS 继承的方法,尝试获取锁(公平锁实现)
protected final boolean tryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取 AQS 的 state(表示锁的状态)
int c = getState();
// 如果 state == 0(锁未被占用)
if (c == 0) {
// 检查队列中是否有等待的节点
if (!hasQueuedPredecessors() &&
// CAS 尝试获取锁
compareAndSetState(0, acquires)) {
// 成功获取锁,设置当前线程为锁的持有者
setExclusiveOwnerThread(current);
return true;
}
}
// 如果当前线程已经是锁的持有者(可重入)
else if (current == getExclusiveOwnerThread()) {
// 计算重入后的 state
int nextc = c + acquires;
// 防止溢出
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
// 更新 state
setState(nextc);
return true;
}
// 获取锁失败
return false;
}
// 检查队列中是否有等待的节点(公平锁核心逻辑)
public final boolean hasQueuedPredecessors() {
// 等待队列的头节点
Node h = head;
// 等待队列的第二个节点
Node s = tail;
// 检查队列是否有等待的节点
return h != null && s != null && h.next != s && s.thread != Thread.currentThread();
}
}
条件变量实现原理
await流程
每个条件变量其实就对应着一个等待队列,其实现类是 ConditionObject
开始 Thread-0 持有锁,调用 await,进入 ConditionObject 的 addConditionWaiter 流程
java
public final void await() throws InterruptedException {
// 1. 检查当前线程是否被中断
if (Thread.interrupted())
throw new InterruptedException();
// 2. 将当前线程加入 Condition 的等待队列
Node node = addConditionWaiter();
// 3. 完全释放当前持有的锁(可重入锁需释放所有重入次数)
int savedState = fullyRelease(node);
// 4. 标记中断模式(0 表示未中断)
int interruptMode = 0;
// 5. 循环检查:当前节点是否已转移到 AQS 同步队列
while (!isOnSyncQueue(node)) {
// 5.1 挂起当前线程(等待被 signal 或中断唤醒)
LockSupport.park(this);
// 5.2 检查线程是否在等待过程中被中断
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
// 若被中断,跳出循环,处理中断逻辑
break;
}
// 6. 线程被唤醒后,尝试重新获取锁(进入 AQS 同步队列的自旋逻辑)
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
// 7. 清理 Condition 等待队列中的取消节点
if (node.nextWaiter != null) // 若有后续节点,清理取消的节点
unlinkCancelledWaiters();
// 8. 根据中断模式处理中断
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
创建新的 Node 状态为 -2(Node.CONDITION),关联 Thread-0,加入等待队列尾部
java
private Node addConditionWaiter() {
// 1. 获取 Condition 等待队列的尾节点(lastWaiter)
Node t = lastWaiter;
// 2. 检查尾节点是否有效(未被取消)
// 如果尾节点存在,且其 waitStatus 不是 Node.CONDITION(说明已被取消)
if (t != null && t.waitStatus != Node.CONDITION) {
// 清理 Condition 队列中已取消的节点(unlinkCancelledWaiters 会遍历队列,移除 waitStatus != CONDITION 的节点)
unlinkCancelledWaiters();
// 重新获取尾节点(清理后可能变化)
t = lastWaiter;
}
// 3. 创建新节点,封装当前线程,状态为 Node.CONDITION
Node node = new Node(Thread.currentThread(), Node.CONDITION);
// 4. 将新节点加入 Condition 等待队列
if (t == null) {
// 如果队列为空(t == null),新节点作为第一个等待节点(firstWaiter)
firstWaiter = node;
} else {
// 如果队列不为空,将新节点链到尾节点的 nextWaiter
t.nextWaiter = node;
}
// 更新尾节点为新创建的节点
lastWaiter = node;
// 5. 返回新创建的节点
return node;
}

接下来进入 AQS 的 fullyRelease 流程,释放同步器上的锁
java
private Node addConditionWaiter() {
// 1. 获取 Condition 等待队列的尾节点(lastWaiter)
Node t = lastWaiter;
// 2. 检查尾节点是否有效(未被取消)
// 如果尾节点存在,且其 waitStatus 不是 Node.CONDITION(说明已被取消)
if (t != null && t.waitStatus != Node.CONDITION) {
// 清理 Condition 队列中已取消的节点(unlinkCancelledWaiters 会遍历队列,移除 waitStatus != CONDITION 的节点)
unlinkCancelledWaiters();
// 重新获取尾节点(清理后可能变化)
t = lastWaiter;
}
// 3. 创建新节点,封装当前线程,状态为 Node.CONDITION
Node node = new Node(Thread.currentThread(), Node.CONDITION);
// 4. 将新节点加入 Condition 等待队列
if (t == null) {
// 如果队列为空(t == null),新节点作为第一个等待节点(firstWaiter)
firstWaiter = node;
} else {
// 如果队列不为空,将新节点链到尾节点的 nextWaiter
t.nextWaiter = node;
}
// 更新尾节点为新创建的节点
lastWaiter = node;
// 5. 返回新创建的节点
return node;
}

unpark AQS 队列中的下一个节点,竞争锁,假设没有其他竞争线程,那么 Thread-1 竞争成功

park 阻塞 Thread-0

signal流程
java
public final void signal() {
// 1. 检查当前线程是否持有锁(确保调用 signal 的线程是锁的持有者)
if (!isHeldExclusively())
// 如果当前线程未持有锁,抛出异常
throw new IllegalMonitorStateException();
// 2. 获取 Condition 等待队列的头节点(firstWaiter)
Node first = firstWaiter;
// 3. 如果头节点存在,唤醒该节点对应的线程
if (first != null)
// 调用 doSignal 处理唤醒逻辑
doSignal(first);
}
假设 Thread-1 要来唤醒 Thread-0
进入 ConditionObject 的 doSignal 流程,取得等待队列中第一个 Node,即 Thread-0 所在 Node
java
private void doSignal(Node first) {
// 循环处理 Condition 队列的节点
do {
// 1. 将头节点后移:firstWaiter 指向原头节点的下一个节点
if ((firstWaiter = first.nextWaiter) == null)
// 如果后移后队列为空,更新 lastWaiter 为 null
lastWaiter = null;
// 2. 断开原头节点的 nextWaiter 链接(避免重复唤醒)
first.nextWaiter = null;
// 3. 尝试将节点从 Condition 队列转移到 AQS 同步队列
// 如果 transferForSignal 失败(如节点已取消),继续尝试下一个节点
} while (!transferForSignal(first) &&
// 获取新的头节点,继续循环
(first = firstWaiter) != null);
}

执行 transferForSignal 流程,将该 Node 加入 AQS 队列尾部,将 Thread-0 的 waitStatus 改为 0,Thread-3 的 waitStatus 改为 -1
java
final boolean transferForSignal(Node node) {
// 1. 尝试将节点的状态从 Node.CONDITION 改为 0(通过 CAS)
// - Node.CONDITION 是节点在 Condition 队列中的状态
// - 改为 0 表示节点即将转移到 AQS 同步队列
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false; // 状态修改失败(节点可能已被取消)
// 2. 将节点加入 AQS 同步队列(enq 会自旋确保节点入队)
Node p = enq(node);
int ws = p.waitStatus; // 获取前驱节点的状态
// 3. 处理前驱节点的状态
// - 如果前驱节点状态是 CANCELLED(ws > 0),或 CAS 修改前驱状态为 SIGNAL 失败
// - 直接唤醒当前节点对应的线程,让其重新参与锁竞争
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true; // 转移成功
}

Thread-1 释放锁,进入 unlock 流程,略