文章目录
1. AQS概述
AQS是一个抽象类,采用模板方法模式,定义了同步器的核心结构。子类通过继承AQS并实现其抽象方法(如tryAcquire
、tryRelease
)来管理同步状态,而AQS负责线程的排队、阻塞与唤醒。
2. 核心组成
2.1 同步状态(State)
- 通过
volatile int state
变量表示资源状态。 - 子类通过CAS操作修改
state
,实现资源的获取与释放。 - 例如:
- ReentrantLock:
state
表示持有锁的线程的重入次数。 - Semaphore:
state
表示可用许可数。
- ReentrantLock:
2.2 等待队列(CLH变种)
- 使用双向链表实现FIFO队列,管理等待线程。
- 每个节点(
Node
)封装一个线程及其状态(如CANCELLED
、SIGNAL
)。 - 独占模式与共享模式节点共存于同一队列,通过
Node.nextWaiter
区分。
3. 关键方法
3.1 模板方法
- 获取资源:
acquire(int arg)
:独占式获取,忽略中断。acquireShared(int arg)
:共享式获取。
- 释放资源:
release(int arg)
:独占式释放。releaseShared(int arg)
:共享式释放。
3.2 需子类实现的方法
tryAcquire(int arg)
:尝试独占获取。tryRelease(int arg)
:尝试独占释放。tryAcquireShared(int arg)
:尝试共享获取(返回剩余资源数)。tryReleaseShared(int arg)
:尝试共享释放(返回是否成功)。
4. 工作流程
4.1 获取资源(以独占模式为例)
- 调用
tryAcquire
尝试直接获取资源。 - 若失败,将线程封装为节点加入队列尾部(
addWaiter
)。 - 自旋检查前驱节点是否为头节点,并尝试获取资源(
acquireQueued
)。 - 若成功,将当前节点设为头节点;否则,挂起线程(
LockSupport.park
)。
4.2 释放资源
- 调用
tryRelease
修改state
。 - 唤醒队列中下一个有效节点(
unparkSuccessor
)。
5. 公平性与非公平性
- 非公平锁:线程可直接插队尝试获取资源(
tryAcquire
中不检查队列)。 - 公平锁:仅在队列为空或当前线程是头节点时才尝试获取(
hasQueuedPredecessors()
检查)。
6. 条件变量(ConditionObject)
- 每个条件变量对应一个等待队列。
- await():释放锁,线程加入条件队列并阻塞。
- signal():将节点从条件队列转移到主同步队列,等待获取锁。
7. 关键设计点
- CAS操作:用于安全修改
state
和队列结构(如compareAndSetState
、compareAndSetTail
)。 - 自旋与挂起:减少上下文切换,仅在必要时挂起线程(如
acquireQueued
中的自旋检查)。 - 传播机制:共享模式下,释放资源时唤醒后续多个节点(
doReleaseShared
)。
8. 典型应用
- ReentrantLock:通过
state
实现可重入,区分公平/非公平。 - Semaphore:共享模式控制并发数。
- CountDownLatch:一次性屏障,
state
初始为计数。 - ReentrantReadWriteLock:读写锁,高16位表示读锁,低16位表示写锁。
9. 示例代码(自定义互斥锁)
class Mutex extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int arg) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
if (getState() == 0) throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0);
return true;
}
public void lock() { acquire(1); }
public void unlock() { release(1); }
}