在 Java 并发包中,ReentrantLock 与 ReentrantReadWriteLock 是常用的同步工具,它们虽然使用方式不同,但底层都构建在 AbstractQueuedSynchronizer(AQS)之上。AQS 通过独占模式和共享模式分别支撑了 Lock 与读写锁的实现,对比其源码能帮助我们看清二者的底层共性。

一、AQS 的基本设计
AQS 使用一个 volatile 的 int 状态变量和 FIFO 同步队列来管理线程的阻塞与唤醒。子类只需实现 tryAcquire、tryRelease(独占)或 tryAcquireShared、tryReleaseShared(共享)等钩子方法,其余的排队、挂起、唤醒由 AQS 父类统一完成。
二、独占模式与 Lock 的对应
以 ReentrantLock 为例,其内部 Sync 类重写了 tryAcquire。下面是一段简化后的独占模式获取锁逻辑:
// 简化版独占模式尝试获取锁
protected boolean tryAcquire(int arg) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 使用 CAS 抢锁
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(current);
return true;
}
} else if (current == getExclusiveOwnerThread()) {
// 可重入
setState(c + 1);
return true;
}
return false;
}
独占模式下,acquire 方法会调用 tryAcquire,失败则进入队列。Lock 的底层共性在于:它完全复用 AQS 的队列与 CAS 机制,只关心“谁能独占状态”。
三、共享模式与 ReadWriteLock 的对应
ReentrantReadWriteLock 的读锁使用共享模式。其 Sync 实现了 tryAcquireShared,允许多个线程同时获取读锁:
// 简化版共享模式尝试获取读锁
protected int tryAcquireShared(int unused) {
int c = getState();
// 写锁被占用且不是当前线程则失败
if (exclusiveCount(c) != 0 && getExclusiveOwnerThread() != Thread.currentThread()) {
return -1;
}
int r = sharedCount(c);
if (r < MAX_COUNT && compareAndSetState(c, c + SHARED_UNIT)) {
return 1; // 成功,正数表示可共享
}
return -1;
}
共享模式中,acquireShared 会循环尝试并传播唤醒后续读线程。写锁则仍走独占逻辑。
四、源码对比看底层共性
从源码层面看,独占与共享有如下对应关系:
| 维度 | 独占模式(Lock) | 共享模式(ReadWriteLock 读锁) |
|---|---|---|
| 尝试获取 | tryAcquire 返回 boolean | tryAcquireShared 返回 int |
| 入队方法 | acquire | acquireShared |
| 唤醒逻辑 | 释放后唤醒下一个节点 | 释放后传播唤醒连续读节点 |
| 底层支撑 | 同一同步队列、CAS 修改 state、LockSupport 挂起 | |
可以看到,无论独占还是共享,AQS 都用同一个队列和 state 变量,通过不同的返回值约定来区分行为。这就是 Lock 与 ReadWriteLock 的底层共性:它们都只是 AQS 钩子方法的不同实现,核心调度完全一致。
五、小结
通过对比 AQS 独占与共享模式的源码,我们能明确:Lock 与 ReadWriteLock 并非两套独立体系,而是共用 AQS 的排队与同步基础设施。理解这一点,有助于在选型时看清性能特征,也能在排查锁竞争时从队列与状态变更角度入手。
AQSLockReadWriteLock修改时间:2026-07-26 07:48:20