AQS 的模板方法是一系列 public final 方法,它们定义了线程获取与释放同步状态的固定算法骨架。子类无法重写它们,只能通过实现钩子方法来嵌入自己的同步逻辑。
以下按照独占模式 、共享模式 和可中断/超时变体三个维度,对核心模板方法进行源码级详解。
一、独占模式模板方法
1. acquire(int arg)
作用 :独占式获取资源,不响应中断,失败则进入队列等待直到成功。
java
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
内部调用链:
tryAcquire(arg):钩子方法,快速尝试获取锁。addWaiter(Node.EXCLUSIVE):获取失败时,将当前线程包装成独占模式 节点,原子地加入 CLH 队列尾部。acquireQueued(node, arg):进入自旋 + 阻塞 循环,直到获取锁成功。- 若前驱是
head,则再次尝试tryAcquire。 - 否则,将前驱状态设为
SIGNAL,然后LockSupport.park(this)阻塞自己。 - 被唤醒后继续循环。
- 若前驱是
selfInterrupt():如果在等待过程中被中断过,acquireQueued返回true,此时补偿一次中断标记。
关键点 :该方法忽略中断,即线程在等待中被中断时不会抛出异常,只是记录中断状态。
2. acquireInterruptibly(int arg)
作用 :独占式获取资源,响应中断 。若在等待中被中断,则抛出 InterruptedException。
java
public final void acquireInterruptibly(int arg) throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
doAcquireInterruptibly 核心逻辑:
java
private void doAcquireInterruptibly(int arg) throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null;
return;
}
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
// 关键差异:检测到中断时直接抛出异常
throw new InterruptedException();
}
} catch (Throwable t) {
cancelAcquire(node);
throw t;
}
}
与 acquire 的区别 :在 parkAndCheckInterrupt() 返回 true(表示被中断唤醒)时,直接抛出异常,而非仅记录中断状态。
3. tryAcquireNanos(int arg, long nanosTimeout)
作用 :独占式获取资源,响应中断且支持超时 。超时未获取到则返回 false。
核心逻辑:
- 计算剩余超时时间
deadline = System.nanoTime() + nanosTimeout。 - 在循环中:
- 检查中断。
- 尝试获取锁。
- 若剩余时间 ≤
spinForTimeoutThreshold(1000 纳秒),则不再阻塞,改为自旋等待,避免阻塞/唤醒的开销大于超时时间。 - 否则
LockSupport.parkNanos(this, nanosTimeout)阻塞指定时间。
- 超时后返回
false,并取消节点。
4. release(int arg)
作用:独占式释放资源。
java
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。 - 若返回
true(资源完全释放),则获取当前head节点。 - 若
head存在且状态不为 0(通常为SIGNAL),则调用unparkSuccessor(h)唤醒后继节点。
二、共享模式模板方法
1. acquireShared(int arg)
作用 :共享式获取资源,不响应中断。
java
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
doAcquireShared 核心逻辑:
java
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED);
boolean interrupted = false;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
// 获取成功,设置头节点并向后传播唤醒
setHeadAndPropagate(node, r);
p.next = null;
if (interrupted) selfInterrupt();
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
interrupted = true;
}
} catch (Throwable t) {
cancelAcquire(node);
throw t;
}
}
关键差异 setHeadAndPropagate:
- 获取成功后,不仅将自己设为
head,还会检查tryAcquireShared的返回值r。 - 若
r > 0(表示还有剩余资源),则调用doReleaseShared()继续唤醒下一个共享节点 ,实现链式唤醒传播 。这是CountDownLatch和Semaphore能一次性唤醒一批线程的原因。
2. acquireSharedInterruptibly(int arg)
作用 :共享式获取,响应中断 。逻辑与 acquireShared 类似,但在检测到中断时抛出 InterruptedException。
3. tryAcquireSharedNanos(int arg, long nanosTimeout)
作用 :共享式获取,响应中断且支持超时。
4. releaseShared(int arg)
作用:共享式释放资源。
java
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
doReleaseShared 核心逻辑:
- 自旋 + CAS 修改
head.waitStatus。 - 若
head状态为SIGNAL,则 CAS 将其改为 0,并唤醒后继节点。 - 若
head状态为 0,则 CAS 将其改为PROPAGATE(传播状态),确保唤醒动作能传播到后续节点。
三、条件队列相关模板方法
AQS 内部还有一个 ConditionObject 类,实现了 Condition 接口,用于精细化线程调度。其模板方法包括:
| 方法 | 作用 |
|---|---|
await() |
释放锁,进入条件队列阻塞,等待 signal |
signal() |
将条件队列中的第一个节点转移到同步队列,使其有机会重新获取锁 |
signalAll() |
将所有条件队列节点转移到同步队列 |
这些方法也遵循模板方法模式,内部调用 isHeldExclusively() 钩子检查独占状态,并操作条件队列(单向链表)。
四、模板方法执行全链路图解
以独占锁 acquire 成功和失败两条路径为例,展示模板方法如何串联钩子方法与底层队列:
五、模板方法总结对照表
| 模板方法 | 模式 | 响应中断 | 支持超时 | 核心内部调用 |
|---|---|---|---|---|
acquire |
独占 | ❌ | ❌ | tryAcquire → addWaiter → acquireQueued |
acquireInterruptibly |
独占 | ✅ | ❌ | tryAcquire → doAcquireInterruptibly |
tryAcquireNanos |
独占 | ✅ | ✅ | tryAcquire → doAcquireNanos |
release |
独占 | --- | --- | tryRelease → unparkSuccessor |
acquireShared |
共享 | ❌ | ❌ | tryAcquireShared → doAcquireShared → setHeadAndPropagate |
acquireSharedInterruptibly |
共享 | ✅ | ❌ | 同上,但中断抛异常 |
tryAcquireSharedNanos |
共享 | ✅ | ✅ | 同上,加超时控制 |
releaseShared |
共享 | --- | --- | tryReleaseShared → doReleaseShared |
这些模板方法构成了 AQS 的并发调度引擎 ,它们确保所有基于 AQS 的同步组件在排队策略、阻塞唤醒、中断处理、超时控制 上行为一致且性能最优。子类只需专注于资源获取与释放的业务判定即可。