一、ReentrantLock的类结构:组合模式的艺术
ReentrantLock的核心代码围绕**AQS(AbstractQueuedSynchronizer)**展开,其类继承关系如下:
public class ReentrantLock implements Lock, java.io.Serializable {
private final Sync sync; // 关键:所有操作委托给Sync对象
abstract static class Sync extends AbstractQueuedSynchronizer { /*...*/ }
static final class NonfairSync extends Sync { /*...*/ } // 非公平锁
static final class FairSync extends Sync { /*...*/ } // 公平锁
}
设计思想:
- 组合优于继承:ReentrantLock将锁的实现委托给内部类
Sync,而非直接继承AQS,保持灵活性。 - 策略模式:通过
FairSync和NonfairSync实现不同锁策略,用户可通过构造函数选择。
二、核心方法剖析:lock()与unlock()
1. 非公平锁的lock()实现
// NonfairSync.lock()
final void lock() {
if (compareAndSetState(0, 1)) // 直接尝试CAS抢锁(非公平性体现)
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1); // 进入AQS队列
}
// AQS.acquire()
public final void acquire(int arg) {
if (!tryAcquire(arg) && // 调用子类实现的tryAcquire()
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
关键点:
- 非公平性:新线程无需排队,直接尝试插队获取锁。
- tryAcquire逻辑:
// NonfairSync.tryAcquire() protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (compareAndSetState(0, acquires)) { // 再次尝试CAS 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; }
2. 公平锁的tryAcquire()
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;
}
}
// 重入逻辑与非公平锁相同
// ...
}
公平性保证:hasQueuedPredecessors()检查队列中是否有其他线程等待,确保“先到先得”。
三、AQS源码逻辑:同步器的核心骨架
1. AQS的双向队列(CLH变体)
- Node结构:
static final class Node { volatile Node prev; // 前驱节点 volatile Node next; // 后继节点 volatile Thread thread; // 关联线程 int waitStatus; // 状态:CANCELLED(1), SIGNAL(-1), CONDITION(-2) } - 队列管理:
- 入队:
addWaiter()将线程包装为Node,通过CAS插入队尾。 - 出队:头节点释放锁后,唤醒后继节点。
- 入队:
2. acquireQueued():自旋与阻塞
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; // 帮助GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) && // 检查是否需要阻塞
parkAndCheckInterrupt()) // 调用LockSupport.park()
interrupted = true;
}
} finally {
if (failed) cancelAcquire(node); // 异常处理
}
}
自旋优化:线程在阻塞前多次尝试获取锁,减少上下文切换开销。
3. unlock()与锁释放
// ReentrantLock.unlock()
public void unlock() {
sync.release(1); // 调用AQS.release()
}
// AQS.release()
public final boolean release(int arg) {
if (tryRelease(arg)) { // 调用子类实现的tryRelease()
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h); // 唤醒后继节点
return true;
}
return false;
}
// 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) { // 完全释放锁
free = true;
setExclusiveOwnerThread(null);
}
setState(c); // 无需CAS,只有持有锁的线程能调用release
return free;
}
唤醒逻辑:unparkSuccessor()找到下一个有效节点,调用LockSupport.unpark()唤醒线程。
四、AQS的设计哲学:模板方法模式
AQS通过模板方法模式将同步逻辑抽象为两类方法:
- 需子类实现:
tryAcquire(int arg):尝试获取资源。tryRelease(int arg):尝试释放资源。
- AQS实现:
- 线程排队、阻塞/唤醒、超时与中断处理。
优势:
- 复用性:AQS作为基类,可扩展为多种同步工具(如Semaphore、CountDownLatch)。
- 关注点分离:子类只需关注资源状态管理,无需处理线程调度细节。
五、总结:ReentrantLock与AQS的协作全景
- ReentrantLock通过组合AQS实现锁功能,将并发控制的核心逻辑委托给Sync对象。
- AQS提供线程排队与调度框架,通过
state和双向队列管理资源竞争。 - 公平性与非公平性是策略选择的结果,体现在
tryAcquire()的实现差异。 - 重入性通过累加
state实现,释放时需完全归零才会唤醒其他线程。
学习启示:理解AQS是掌握Java并发包的关键,其设计体现了“分离变与不变”的软件工程思想。