一、AQS 组成
1. 同步状态(state
)
- 作用:表示资源的可用状态(如锁的持有次数、信号量的许可数量等)。
- 操作:通过
volatile
+ CAS(compareAndSetState
)保证原子性。 - 示例:
ReentrantLock
:state=0
表示未锁定,state>0
表示锁定次数。Semaphore
:state
表示剩余许可数量。
2. 等待队列
- 结构:双向链表,每个节点封装一个等待线程。
- 节点状态:
CANCELLED
:线程已取消等待。SIGNAL
:当前节点释放资源后需唤醒后继节点。CONDITION
:节点在条件队列中等待。PROPAGATE
:共享模式下传播唤醒。
二、AQS 的两种模式
1. 独占模式(Exclusive)
- 特点:同一时刻仅一个线程可获取资源(如
ReentrantLock
)。 - 关键方法:
acquire(int arg)
:获取资源(不可中断)。acquireInterruptibly(int arg)
:可中断获取。tryAcquire(int arg)
:需子类实现(定义资源获取逻辑)。release(int arg)
:释放资源,唤醒后继线程。
2. 共享模式(Shared)
- 特点:多个线程可同时获取资源(如
Semaphore
、CountDownLatch
)。 - 关键方法:
acquireShared(int arg)
:获取共享资源。tryAcquireShared(int arg)
:需子类实现。releaseShared(int arg)
:释放资源并唤醒等待线程。
三、AQS 工作流程(以独占模式为例)
1. 线程获取资源(acquire
)
public final void acquire(int arg) {
if (!tryAcquire(arg) && // 尝试直接获取资源
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 失败则入队阻塞
selfInterrupt(); // 恢复中断状态
}
- 步骤:
- 尝试获取资源:调用子类实现的
tryAcquire
。 - 入队等待:若失败,将线程封装为
Node
加入队列尾部。 - 阻塞线程:通过
LockSupport.park()
挂起线程,等待唤醒。
- 尝试获取资源:调用子类实现的
2. 线程释放资源(release
)
public final boolean release(int arg) {
if (tryRelease(arg)) { // 子类实现释放逻辑
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h); // 唤醒后继节点线程
return true;
}
return false;
}
- 步骤:
- 释放资源:调用子类实现的
tryRelease
。 - 唤醒后继:找到队列中第一个未取消的节点,通过
LockSupport.unpark()
唤醒线程。
- 释放资源:调用子类实现的
四、AQS 的条件变量(Condition
)
通过 ConditionObject
实现类似 wait/notify
的等待/通知机制:
- 条件队列:每个
Condition
维护一个单向链表队列。 - 关键方法:
await()
:释放锁并加入条件队列。signal()
:将条件队列中的节点转移到同步队列,等待获取锁。
五、AQS 的应用示例
1. 实现不可重入锁
class Mutex implements Lock {
private final Sync sync = new Sync();
static class Sync extends AbstractQueuedSynchronizer {
protected boolean tryAcquire(int arg) {
if (compareAndSetState(0, 1)) { // state=0 表示未锁定
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
protected boolean tryRelease(int arg) {
if (getState() == 0) throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0); // 释放锁
return true;
}
}
// 其他 Lock 接口方法委托给 sync
public void lock() { sync.acquire(1); }
public void unlock() { sync.release(1); }
// ...
}