3.2 AQS:排队上厕所的哲学(手写简易版ReentrantLock)
一、AQS是啥?厕所排队神器了解一下
想象一下高峰期的商场厕所,门口永远排着长队。AQS (AbstractQueuedSynchronizer) 就相当于厕所的排队管理系统,它负责:
- 维护排队状态:厕所坑位是否空闲 (state变量)
- 管理等待队列:谁在排队,排队的顺序 (FIFO队列)
- 叫号机制:坑位空了,按顺序叫号 (线程唤醒)
AQS的核心思想:状态state + FIFO等待队列
// AbstractQueuedSynchronizer源码核心字段
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer {
private volatile int state; // 同步状态,厕所坑位状态
private transient volatile Node head; // FIFO等待队列头节点
private transient volatile Node tail; // FIFO等待队列尾节点
...
}
二、ReentrantLock:厕所带锁豪华版 (手写简易版ReentrantLock)
ReentrantLock 就是基于 AQS 实现的“豪华版厕所”,它比 synchronized 这个“公厕”更灵活,功能更强大。
ReentrantLock 的核心组件:内部维护了一个 Sync 抽象类,Sync 继承自 AQS。
public class ReentrantLock implements Lock, java.io.Serializable {
private final Sync sync; // 内部同步器,继承自AQS
abstract static class Sync extends AbstractQueuedSynchronizer { ... }
static final class NonfairSync extends Sync { ... } // 非公平锁实现
static final class FairSync extends Sync { ... } // 公平锁实现
...
}
手写简易版 ReentrantLock (MyReentrantLock.java)
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
public class MyReentrantLock implements Lock, java.io.Serializable {
private final Sync sync;
// 默认非公平锁
public MyReentrantLock() {
sync = new NonfairSync();
}
// 可选公平锁
public MyReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
// ============ Sync 抽象类 (AQS子类) ============
private abstract static class Sync extends AbstractQueuedSynchronizer {
// 尝试获取锁
@Override
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState(); // 获取当前state值
if (c == 0) { // state为0,表示锁未被占用
if (compareAndSetState(0, acquires)) { // CAS尝试设置state为acquires (通常为1)
setExclusiveOwnerThread(current); // 设置独占锁持有线程
return true; // 获取锁成功
}
} else if (current == getExclusiveOwnerThread()) { // 重入逻辑:当前线程已持有锁
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc); // 增加重入次数
return true; // 获取锁成功
}
return false; // 获取锁失败
}
// 尝试释放锁
@Override
protected final boolean tryRelease(int releases) {
int c = getState() - releases; // 减少state值
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
if (c == 0) { // state减为0,表示锁完全释放
setExclusiveOwnerThread(null); // 清空独占锁持有线程
setState(0); // 设置state为0
return true; // 释放锁成功
}
setState(c); // 减少重入次数,但锁未完全释放
return false; // 释放锁失败 (锁还被持有)
}
// 是否持有独占锁
@Override
protected final boolean isHeldExclusively() {
return getExclusiveOwnerThread() == Thread.currentThread();
}
// ConditionObject,用于支持Condition
final ConditionObject newCondition() {
return new ConditionObject();
}
// 获取独占锁
final void acquire(int acquires) {
if (!tryAcquire(acquires) && // 尝试获取锁,失败则进入等待队列
acquireQueued(addWaiter(Node.EXCLUSIVE), acquires)) // 加入等待队列并阻塞
selfInterrupt();
}
// 释放独占锁
final boolean release(int releases) {
return tryRelease(releases) && tryReleaseShared(releases); // 尝试释放锁,并唤醒后继节点
}
// 获取Condition
final ConditionObject newCondition() {
return new ConditionObject();
}
}
// ============ 非公平锁实现 ============
static final class NonfairSync extends Sync {
@Override
final void lock() {
if (compareAndSetState(0, 1)) // 尝试CAS直接获取锁,不排队
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1); // CAS失败,老老实实排队去
}
@Override
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires); // 调用父类Sync的非公平获取逻辑
}
}
// ============ 公平锁实现 ============
static final class FairSync extends Sync {
@Override
final void lock() {
acquire(1); // 公平锁直接排队获取
}
@Override
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;
}
}
// ============ Lock 接口方法实现 ============
@Override
public void lock() {
sync.lock(); // 调用Sync的lock方法
}
@Override
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
@Override
public boolean tryLock() {
return sync.nonfairTryAcquire(1); // 非公平尝试获取
}
@Override
public boolean tryLock(long time, java.util.concurrent.TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(time));
}
@Override
public void unlock() {
sync.release(1); // 调用Sync的release方法
}
@Override
public Condition newCondition() {
return sync.newCondition(); // 创建Condition
}
// 非公平尝试获取锁 (Sync父类方法)
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;
}
}
代码解析:
- Sync 抽象类:继承了 AQS,负责实现锁的核心逻辑(获取、释放、状态管理)。
- NonfairSync (非公平锁):
lock()方法:上来先尝试 CAS 抢锁,抢不到再老实排队 (acquire)。tryAcquire()方法:复用父类Sync的非公平获取逻辑。
- FairSync (公平锁):
lock()方法:直接排队 (acquire),不参与抢锁。tryAcquire()方法:先检查队列是否有等待线程 (hasQueuedPredecessors()),有则排队,没有则尝试 CAS 抢锁。
- Lock 接口方法:
lock(),unlock(),tryLock()等方法,实际调用Sync类的方法。
测试代码 (MyReentrantLockTest.java)
public class MyReentrantLockTest {
private static MyReentrantLock lock = new MyReentrantLock(true); // 使用公平锁
private static int counter = 0;
public static void main(String[] args) throws InterruptedException {
Runnable task = () -> {
for (int i = 0; i < 10000; i++) {
lock.lock(); // 获取锁
try {
counter++; // 临界区:计数器自增
} finally {
lock.unlock(); // 释放锁
}
}
};
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("Counter value: " + counter); // 输出最终计数器值 (应为20000)
}
}
运行结果 (示例):
Counter value: 20000
三、AQS核心方法解读
1. tryAcquire(int acquires) (尝试获取独占锁)
- 作用:尝试获取锁,但不阻塞。
- 返回值:成功返回
true,失败返回false。 - 实现要点:
- 检查同步状态 (state)。
- CAS 修改 state。
- 设置独占锁持有线程。
2. tryRelease(int releases) (尝试释放独占锁)
- 作用:尝试释放锁,但不唤醒等待线程。
- 返回值:完全释放返回
true,部分释放返回false。 - 实现要点:
- 减少 state 值。
- 判断是否完全释放 (state == 0)。
- 清空独占锁持有线程。
3. acquire(int acquires) (获取独占锁,会阻塞)
- 作用:获取锁,如果获取不到则进入等待队列阻塞。
- 流程:
tryAcquire()尝试获取锁。- 失败则
addWaiter()加入等待队列。 acquireQueued()在队列中自旋等待被唤醒。
4. release(int releases) (释放独占锁,会唤醒)
- 作用:释放锁,并唤醒等待队列中的后继节点。
- 流程:
tryRelease()尝试释放锁。- 成功则
unparkSuccessor()唤醒后继节点。
四、面试必杀技
-
AQS是什么?
- 答:抽象队列同步器,是构建锁和同步器的框架,核心是 state 状态和 FIFO 等待队列。
-
ReentrantLock 和 synchronized 的区别?
- 答:ReentrantLock 基于 AQS,功能更强大,例如可中断、可公平锁、可绑定 Condition;synchronized 是 JVM 内置关键字,更轻量级,但功能相对简单。
-
公平锁和非公平锁的区别?
- 答:公平锁严格按照 FIFO 队列排队,非公平锁允许线程“插队”抢锁,性能更高但可能导致饥饿。
-
手写一个简易版 ReentrantLock? (恭喜你,前面已经完成了!)
五、防翻车小贴士
- 理解 AQS 的核心是 状态 (state) 和 队列 (FIFO queue)。
- 掌握
tryAcquire和tryRelease的实现逻辑,这是自定义同步器的关键。 - 区分公平锁和非公平锁的实现差异。
- 深入理解
acquire和release方法的阻塞和唤醒流程。
章节总结:
AQS 就像厕所排队系统,ReentrantLock 是豪华带锁厕所。 手写 ReentrantLock 就像自己组装马桶,虽然简易,但核心功能都有了。 掌握 AQS,你就能成为厕所...哦不,是并发世界的架构师!
下一章,我们继续深入并发的修罗场,聊聊线程池的那些事儿!准备好了吗? 😉