ReentrantLock / ReentrantReadWriteLock 锁源码精读
概述
ReentrantLock 是 AQS 独占锁的标准实现,核心是公平 / 非公平两种策略与可重入计数;ReentrantReadWriteLock 是 AQS 混合实现——读锁共享、写锁独占,用一个 int state 拆出高低 16 位同时记录读计数与写计数。本文基于 OpenJDK 21 源码拆解这两把锁。
一、ReentrantLock 的整体结构
// java.util.concurrent.locks.ReentrantLock
public class ReentrantLock implements Lock, Serializable {
private final Sync sync; // 策略对象:公平或非公平
public ReentrantLock() { sync = new NonfairSync(); } // 默认非公平
public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
abstract static class Sync extends AbstractQueuedSynchronizer { ... }
static final class NonfairSync extends Sync { ... }
static final class FairSync extends Sync { ... }
}策略模式:Sync 封装共享逻辑(重入、释放、非公平尝试),FairSync / NonfairSync 只重写 lock() 与 tryAcquire() 的公平性差异。
二、非公平锁:NonfairSync
2.1 lock() 的 CAS 抢占
// ReentrantLock.NonfairSync
final void lock() {
// 先尝试一次"插队":CAS 0 → 1
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread()); // 抢到直接设持有者
else
acquire(1); // 失败走 AQS 标准流程(tryAcquire → 入队)
}非公平的体现:lock() 进来先无条件 CAS 抢一次,不检查队列里有没有人排队;acquire 内部的 tryAcquire 再次允许插队。
2.2 tryAcquire 的插队尝试
// ReentrantLock.Sync
final boolean nonfairTryAcquire(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;
if (nextc < 0) throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}非公平锁可能"饿死"长时间排队的线程,但换来了更少的线程挂起/唤醒(吞吐更高)。
三、公平锁:FairSync 与 hasQueuedPredecessors
// ReentrantLock.FairSync
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; // 重入不检查队列
if (nextc < 0) throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
// AQS.hasQueuedPredecessors
public final boolean hasQueuedPredecessors() {
Node h = head, t = tail;
Node s;
return h != t && // 队列非空
((s = h.next) == null || // 或头结点后继暂未建立
s.thread != Thread.currentThread()); // 且队首排队者不是自己
}hasQueuedPredecessors 的返回值含义:true = 有比当前线程更早排队的线程,应当排队;false = 队列为空或队首就是自己(重入场景放行)。
注意
s == null也算"有前驱"——这是头尾节点建立链接过程中的竞态窗口,宁可多排一次队也不破坏公平。
四、可重入与 tryLock
4.1 lock() 的可重入
ReentrantLock 的可重入完全由 state 承担:每次重入 state + 1,每次 unlock 减 1,减到 0 才真正释放并唤醒后继。
// ReentrantLock.unlock → AQS.release
public void unlock() { sync.release(1); }
// ReentrantLock.Sync.tryRelease
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) { // 减到 0 才真正释放
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}可重入上限:
state是int,重入次数超过Integer.MAX_VALUE会溢出为负,源码用nextc < 0抛出Error兜底。
4.2 tryLock() 的非阻塞尝试
// ReentrantLock
public boolean tryLock() {
return sync.nonfairTryAcquire(1); // 走非公平尝试:CAS 抢一次,不排队
}即使锁是公平锁,tryLock() 也默认走非公平逻辑(文档明确说明)——因为"尝试一下,拿不到就走"的语义不应当排队阻塞。而带超时的 tryLock(long, TimeUnit) 则按锁的公平策略处理:
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}五、ReentrantReadWriteLock 的 state 高低位拆分
5.1 位拆分
// java.util.concurrent.locks.ReentrantReadWriteLock
static final int SHARED_SHIFT = 16;
static final int SHARED_UNIT = (1 << SHARED_SHIFT); // 读锁每 +1 = state + 65536
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1; // 65535 上限
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
static int sharedCount(int c) { return c >>> SHARED_SHIFT; } // 高 16 位:读锁计数
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; } // 低 16 位:写锁计数一个 int state 同时装两种计数:高 16 位读锁持有数、低 16 位写锁持有数(写锁只允许一个线程,低 16 位实际记录重入次数)。写锁计数与读锁计数不会互相冲突:写锁加 1 只动低 16 位,读锁加 1 只加 65536。
5.2 内部结构
public class ReentrantReadWriteLock implements ReadWriteLock, Serializable {
private final ReentrantLock readerLock; // 读锁(共享)
private final ReentrantLock writerLock; // 写锁(独占)
final Sync sync;
static abstract class Sync extends AbstractQueuedSynchronizer { ... }
// HoldCounter 与 ThreadLocalHoldCounter 见第七节
}六、WriteLock 与 ReadLock 的获取
6.1 WriteLock.lock() 的互斥
// ReentrantReadWriteLock.Sync.tryAcquire
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount(c);
if (c != 0) {
// 有读锁(c 高 16 位非 0)或有其他线程持有写锁 → 失败
if (w == 0 || current != getExclusiveOwnerThread())
return false;
// 写锁重入
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
setState(c + acquires);
return true;
}
// state == 0:无人持锁 → 需要写阻塞策略(公平性)再 CAS
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}写锁互斥规则:只要有读锁持有(c >>> 16 != 0),写锁一定失败——写锁必须等所有读锁释放;写锁重入则不受影响。
6.2 ReadLock.lock() 的共享
// ReentrantReadWriteLock.Sync.tryAcquireShared
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();
if (exclusiveCount(c) != 0 && // 有写锁
getExclusiveOwnerThread() != current) // 且不是自己(写锁可降级为读锁)
return -1; // 失败:入队等待
int r = sharedCount(c);
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) { // 读计数 + 65536
// 首次获取 / 重入计数维护(见第七节)
return 1;
}
return fullTryAcquireShared(current); // 慢路径:自旋重试
}写锁持有的线程可以再拿读锁(锁降级),但读锁持有者不能拿写锁。readerShouldBlock() 由公平性决定:公平锁下排队队列非空则读锁也排队(防止写锁饥饿)。
七、ThreadLocalHoldCounter 读锁重入计数
读锁是共享的,每个线程的重入次数需要独立记录,AQS 用 ThreadLocal 实现:
// ReentrantReadWriteLock.Sync
static final class HoldCounter {
int count = 0; // 当前线程的读锁重入次数
final long tid = LockSupport.getThreadId(Thread.currentThread());
}
static final class ThreadLocalHoldCounter
extends ThreadLocal<HoldCounter> {
public HoldCounter initialValue() { return new HoldCounter(); }
}
private transient ThreadLocalHoldCounter readHolds; // 每线程重入计数// tryAcquireShared 成功后的计数维护
// ① 第一个读锁(共享计数为 1)→ 记录 cachedHoldCounter
// ② 非首次 → readHolds.get().count++(ThreadLocal 中 +1)7.1 ReadLock.unlock() 的 tryReleaseShared
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != LockSupport.getThreadId(current))
rh = readHolds.get(); // 取当前线程计数
int count = rh.count;
if (count <= 1) {
readHolds.remove(); // 减到 0 清除 ThreadLocal(防泄漏)
if (count <= 0) throw unmatchedUnlockException();
}
--rh.count;
for (;;) {
int c = getState();
int nextc = c - SHARED_UNIT; // 读计数减 1(-65536)
if (compareAndSetState(c, nextc)) {
if (nextc == 0) // 最后一个读锁释放 → 可唤醒写锁
return true;
return false;
}
}
}最后释放的线程返回 true,releaseShared 会调用 doReleaseShared() 唤醒排队中的写锁(unparkSuccessor),完成读写交替。
八、实现要点
ReentrantLock / 读写锁核心:
非公平锁:lock() 先 CAS 插队 + tryAcquire 再插队
公平锁:hasQueuedPredecessors 检测队列后排队
可重入:state 累加/递减,减到 0 才真正释放
tryLock:非阻塞 CAS(不排队),带超时走公平策略
读写锁:state 高 16 位读计数 + 低 16 位写计数
锁降级:持有写锁可再拿读锁(反之不行)
读重入:ThreadLocalHoldCounter 每线程独立计数
常见陷阱:
读锁之间不互斥,写锁与一切互斥
读锁释放清 ThreadLocal,未配对 unlock 抛异常
写锁等待读锁全部释放(可能被持续读锁饿死)
锁降级顺序:写 → 读(不能读 → 写)