ReentrantLock
ReentrantLock 源码分析
ReentrantLock 解决的问题:给 Java 一把比 synchronized 更可控的独占锁,可中断、可超时、可公平、可挂多个条件队列。
核心思路:锁本身只是门面,全部同步逻辑委托给 AQS 子类 Sync,用 AQS 的 volatile int state 记重入次数,state 从 0 CAS 到 1 即抢锁成功,抢不到就进 AQS 的 CLH 变种队列里 park 等待。
注意:JDK 15 起 ReentrantLock/AQS 被大改过,网上大量教程还是 JDK 8 的 nonfairTryAcquire + acquireQueued 版本,下面是新版真实结构(快路径 initialTryLock 与排队重试 tryAcquire 分离)。
代码块收起展开
// 基于本地 JDK 源码 (D:/1ForCode/JAVA_Source, java.base, JDK 25), java.util.concurrent.locks.ReentrantLock
public class ReentrantLock implements Lock, java.io.Serializable {
// ...
private final Sync sync; // 唯一字段:所有逻辑委托给 AQS 子类,公平/非公平只是换个 Sync 实现
abstract static class Sync extends AbstractQueuedSynchronizer {
// ...
@ReservedStackAccess // 预留栈空间执行完整个方法,防 StackOverflowError 把锁状态改到一半
final boolean tryLock() { // 无参 tryLock() 的实现:永远走非公平逻辑,公平锁调它照样插队
Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, 1)) { // 唯一竞争点:CAS 把 state 0 -> 1
setExclusiveOwnerThread(current); // 必须先 CAS 成功再记 owner,反过来会被别的线程读到假 owner
return true;
}
} else if (getExclusiveOwnerThread() == current) {
if (++c < 0) // overflow // 重入 2^31-1 次才溢出,防御性检查
throw new Error("Maximum lock count exceeded");
setState(c); // 只有持锁线程会写这里,独占写不需要 CAS
return true;
}
return false;
}
abstract boolean initialTryLock(); // 公平/非公平的唯一分歧点:第一次抢锁前要不要看队列
@ReservedStackAccess
final void lock() {
if (!initialTryLock()) // 快路径失败才进 AQS 的排队机器
acquire(1);
}
@ReservedStackAccess
final void lockInterruptibly() throws InterruptedException {
if (Thread.interrupted()) // 进门先查中断:响应中断的优先级高于拿锁,哪怕锁现在就空着
throw new InterruptedException();
if (!initialTryLock())
acquireInterruptibly(1);
}
@ReservedStackAccess
final boolean tryLockNanos(long nanos) throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return initialTryLock() || tryAcquireNanos(1, nanos);
}
@ReservedStackAccess
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (getExclusiveOwnerThread() != Thread.currentThread())
throw new IllegalMonitorStateException(); // 没持锁就 unlock 直接炸:lock() 放 try 里面失败后走 finally,就是这个异常
boolean free = (c == 0);
if (free)
setExclusiveOwnerThread(null); // 先清 owner 再写 state
setState(c); // volatile 写放最后:把 owner=null 一起发布出去,对下一个加锁者 happens-before
return free; // 减到 0 才返回 true,release 才会去唤醒后继;重入未清零时不唤醒任何人
}
// ...
}
static final class NonfairSync extends Sync {
// ...
final boolean initialTryLock() {
Thread current = Thread.currentThread();
if (compareAndSetState(0, 1)) { // first attempt is unguarded // 连 state 都不先读,上来盲 CAS:赌锁大概率空闲,省一次 volatile 读
setExclusiveOwnerThread(current);
return true;
} else if (getExclusiveOwnerThread() == current) {
int c = getState() + 1;
if (c < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(c);
return true;
} else
return false;
}
protected final boolean tryAcquire(int acquires) { // 入队后每次被唤醒都调它
if (getState() == 0 && compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(Thread.currentThread());
return true; // 没有重入分支:持锁线程不可能同时在队里等自己,重入在 initialTryLock 就返回了
}
return false;
}
}
static final class FairSync extends Sync {
// ...
final boolean initialTryLock() {
Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedThreads() && compareAndSetState(0, 1)) { // 公平:队列里有人排队就不抢,直接去排队
setExclusiveOwnerThread(current);
return true;
}
} else if (getExclusiveOwnerThread() == current) { // 重入不受公平性约束,否则持锁线程排队等自己 = 死锁
if (++c < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(c);
return true;
}
return false;
}
protected final boolean tryAcquire(int acquires) {
if (getState() == 0 && !hasQueuedPredecessors() && // 入队后改查"有没有排在我前面的":自己已在队里,用 hasQueuedThreads 会把自己算进去永远失败
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
}
public ReentrantLock() {
sync = new NonfairSync(); // 默认非公平:吞吐优先
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
public void lock() {
sync.lock();
}
public boolean tryLock() {
return sync.tryLock(); // 即使是公平锁也插队;要不插队的非阻塞尝试,用 tryLock(0, TimeUnit)
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryLockNanos(unit.toNanos(timeout));
}
public void unlock() {
sync.release(1);
}
public Condition newCondition() {
return sync.newCondition(); // 一把锁可挂多个条件队列做精准唤醒;synchronized 只有一个 wait set
}
// ...
}AQS 侧只需要看独占模式的三样东西:state、等待队列的 Node、以及 acquire/release 主干。
代码块收起展开
// 基于本地 JDK 源码 (D:/1ForCode/JAVA_Source, java.base, JDK 25), java.util.concurrent.locks.AbstractQueuedSynchronizer
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer // owner 线程存在这个父类里,exclusiveOwnerThread 字段
implements java.io.Serializable {
// ...
// Node status bits, also used as argument and return values
static final int WAITING = 1; // must be 1 // "我要 park 了,释放锁时请 unpark 我"
static final int CANCELLED = 0x80000000; // must be negative // 用符号位当取消标记,status < 0 一次比较判取消
static final int COND = 2; // in a condition wait
/** CLH Nodes */
abstract static class Node {
volatile Node prev; // initially attached via casTail // prev 链才是权威链,入队时随 CAS 一起建立
volatile Node next; // visibly nonnull when signallable // next 是事后补的优化链,可能瞬时缺失,遍历要从 tail 沿 prev 走
Thread waiter; // visibly nonnull when enqueued
volatile int status; // written by owner, atomic bit ops by others
// ...
}
private transient volatile Node head; // 哨兵:head 代表"当前持锁者的位置",它的 next 才是第一个等待者
private transient volatile Node tail;
private volatile int state; // 唯一同步状态。ReentrantLock 里 = 重入次数,Semaphore 里 = 剩余许可,语义由子类定义
public final void acquire(int arg) {
if (!tryAcquire(arg)) // 入队前再给一次机会,这是非公平锁的第二个插队点
acquire(null, arg, false, false, false, 0L); // 排队主循环,所有 acquire 变体共用这一个方法
}
final int acquire(Node node, int arg, boolean shared,
boolean interruptible, boolean timed, long time) {
Thread current = Thread.currentThread();
byte spins = 0, postSpins = 0; // retries upon unpark of first thread
boolean interrupted = false, first = false;
Node pred = null; // predecessor of node when enqueued
// ...
for (;;) {
if (!first && (pred = (node == null) ? null : node.prev) != null &&
!(first = (head == pred))) { // 前驱是 head 才算队首,才有抢锁资格
if (pred.status < 0) {
cleanQueue(); // predecessor cancelled // 前驱取消了,摘掉取消节点后重来
continue;
} else if (pred.prev == null) {
Thread.onSpinWait(); // ensure serialization // 前驱还没完全入队,自旋等链稳定
continue;
}
}
if (first || pred == null) { // 只有队首(或还没入队的新线程)调 tryAcquire:队列中间的线程连试的资格都没有,不惊群
boolean acquired;
try {
if (shared)
acquired = (tryAcquireShared(arg) >= 0);
else
acquired = tryAcquire(arg); // 回调到 NonfairSync/FairSync 的 tryAcquire
} catch (Throwable ex) {
cancelAcquire(node, interrupted, false); // 子类 tryAcquire 抛异常也要把节点摘掉,否则整条队列永久堵死
throw ex;
}
if (acquired) {
if (first) {
node.prev = null; // 自己升级为新 head(哨兵),旧 head 断链帮 GC
head = node;
pred.next = null;
node.waiter = null;
if (shared)
signalNextIfShared(node);
if (interrupted)
current.interrupt(); // 不可中断模式:排队时吃掉的中断在拿到锁后补回标记
}
return 1;
}
}
Node t;
if ((t = tail) == null) { // initialize queue // 队列懒初始化:无竞争时一个节点都不分配
if (tryInitializeHead() == null)
return acquireOnOOME(shared, arg);
} else if (node == null) { // allocate; retry before enqueue
try {
node = (shared) ? new SharedNode() : new ExclusiveNode();
} catch (OutOfMemoryError oome) {
return acquireOnOOME(shared, arg);
}
} else if (pred == null) { // try to enqueue
node.waiter = current;
node.setPrevRelaxed(t); // avoid unnecessary fence
if (!casTail(t, node)) // 入队只 CAS tail 一个点
node.setPrevRelaxed(null); // back out
else
t.next = node; // next 链事后补,所以它不可靠
} else if (first && spins != 0) {
--spins; // reduce unfairness on rewaits // 被唤醒却被插队抢走了?先自旋几圈别急着再睡
Thread.onSpinWait();
} else if (node.status == 0) {
node.status = WAITING; // enable signal and recheck // 先声明"我要睡了",下轮循环再试一次锁,最后才真 park
} else {
spins = postSpins = (byte)((postSpins << 1) | 1); // 每被无效唤醒一次,下次自旋配额翻倍
try {
long nanos;
if (!timed)
LockSupport.park(this); // 线程真正阻塞在这一行
else if ((nanos = time - System.nanoTime()) > 0L)
LockSupport.parkNanos(this, nanos);
else
break; // 超时,跳出去走取消路径返回 0
} catch (Error | RuntimeException ex) {
cancelAcquire(node, interrupted, interruptible); // cancel & rethrow
throw ex;
}
node.clearStatus();
if ((interrupted |= Thread.interrupted()) && interruptible)
break; // 可中断模式:立刻跳出,cancelAcquire 返回负数,外层抛 InterruptedException
}
}
return cancelAcquire(node, interrupted, interruptible);
}
public final boolean release(int arg) {
if (tryRelease(arg)) { // 回调 Sync.tryRelease:state 减到 0 才为 true
signalNext(head); // 只唤醒 head 的后继这一个线程
return true;
}
return false;
}
private static void signalNext(Node h) {
Node s;
if (h != null && (s = h.next) != null && s.status != 0) { // status == 0 说明对方还没准备睡,不用 unpark
s.getAndUnsetStatus(WAITING); // 原子清 WAITING 位,与 park 前"先置位再检查"的协议配对,防唤醒丢失
LockSupport.unpark(s.waiter);
}
}
public final boolean hasQueuedPredecessors() {
Thread first = null; Node h, s;
if ((h = head) != null && ((s = h.next) == null ||
(first = s.waiter) == null ||
s.prev == null))
first = getFirstQueuedThread(); // retry via getFirstQueuedThread // next 链瞬时不一致时,兜底从 tail 沿 prev 反向找队首
return first != null && first != Thread.currentThread(); // 队首是自己 = 没有前驱,公平锁被唤醒后才能通过这关
}
// ...
}原理串讲
拿默认的非公平锁走一遍完整链路。线程 T1 调 lock(),进 Sync.lock(),先走 NonfairSync.initialTryLock():不读 state 直接 compareAndSetState(0, 1),成功后 setExclusiveOwnerThread(T1),整个加锁就两步,队列、Node 什么都没碰。
这就是无竞争时 ReentrantLock 快的原因:懒到极致,连等待队列的 head 节点都是第一次有人排队时才建(tryInitializeHead)。
T2 这时也来 lock()。initialTryLock 里 CAS 失败、owner 又是 T1,返回 false,进 AQS.acquire(1)。
这里先再调一次 tryAcquire(第二个插队点),还是失败,进主循环 acquire(null, 1, false, false, false, 0L)。
循环里依次完成:建 ExclusiveNode、casTail 入队、把 status 置为 WAITING、再绕一圈确认还是抢不到,最后 LockSupport.park(this) 睡死在这一行。
注意置 WAITING 和 park 之间隔了一整轮循环重试。为什么这么设计?因为 signalNext 只对 status != 0 的节点执行 unpark,如果 T2 先检查锁再置位睡觉,T1 可能恰好在”检查完、还没睡”的窗口里释放锁并跳过唤醒,T2 就永远睡下去了。
先声明”我要睡了”、再做最后一次检查、然后才真睡,这是标准的防唤醒丢失协议,和 signalNext 里的 getAndUnsetStatus(WAITING) 严格配对。
T1 干完活调 unlock(),进 AQS.release(1) 回调 Sync.tryRelease(1):先验证调用者就是 owner(不是就抛 IllegalMonitorStateException),state 减 1。
若 T1 是重入了两层的状态,这里减完还剩 1,返回 false,release 直接结束,谁也不唤醒,锁还在 T1 手里。
减到 0 时先 setExclusiveOwnerThread(null) 再 setState(0),volatile 写放在最后一步,把清 owner 的动作一起发布出去。
然后 release 调 signalNext(head),只 unpark head 的后继一个线程,也就是 T2。
为什么只唤醒一个而不是全部?因为独占锁一次只能有一个赢家,全唤醒就是惊群:一堆线程醒来抢一个 CAS,输家再集体睡回去,白付 n-1 次上下文切换。
head 哨兵设计保证了”谁该被唤醒”是 O(1) 可定位的。
T2 从 park 醒来,回到循环顶部,发现自己前驱就是 head,first == true,调 NonfairSync.tryAcquire,CAS 成功。
收尾动作是把自己升级成新 head:node.prev = null; head = node; pred.next = null,自己变成下一任哨兵。
这里还有个细节:非公平模式下 T2 醒来那一瞬间可能被路过的新线程插队抢走锁,此时 T2 不会立刻再 park,而是按 postSpins 翻倍的配额先自旋几圈(“reduce unfairness on rewaits”),用少量空转对冲非公平带来的重复挂起。
公平锁的差别只在两个 try 方法:initialTryLock 用 hasQueuedThreads(),队里有任何人就不抢;入队后的 tryAcquire 换成 hasQueuedPredecessors(),只要求”我前面没人”。
为什么要用两个不同的检查?因为入队后自己就在队列里,继续用 hasQueuedThreads 会把自己算进去,队首线程永远无法通过检查,锁就没人能拿了。
另外重入分支在公平锁里也不检查队列,持锁线程重入必须无条件放行,否则它会去排队等自己释放,直接死锁。
设计取舍
- 默认非公平:锁释放到被唤醒者真正运行之间有几微秒到几十微秒的窗口,让路过的线程直接插队能把这段窗口利用起来,吞吐显著更高;代价是极端竞争下排队者可能长时间抢不到。公平锁把每次唤醒变成严格 FIFO 交接,多一次队列检查外加大量上下文切换,吞吐可差一个数量级。
- 无参
tryLock()故意不检查公平性,公平锁上它也插队,这是文档明说的特性;想要遵守公平性的非阻塞尝试,用tryLock(0, TimeUnit.SECONDS)。 - 重入靠 state 计数,lock 几次必须 unlock 几次;
unlock()放 finally 且lock()写在 try 之外,否则加锁失败也会走 finally 触发 IllegalMonitorStateException。 - JDK 15+ 把重入检查收进
initialTryLock,排队后的tryAcquire不再判断重入:持锁线程不可能在队列里等自己,老版本每次队列重试都白查一次 owner。 - 对比 synchronized:只在需要可中断、超时、公平、多 Condition 之一时才选 ReentrantLock;现代 JVM 下两者无竞争性能相当,synchronized 不用手动释放且有偏向 monitor 的 JIT 优化,能用就优先用。