AQS源码解析:深入剖析Java并发核心框架
本文将深入分析Java并发包中的AbstractQueuedSynchronizer
(AQS),这是Java并发工具(如ReentrantLock
、CountDownLatch
等)的核心框架。我们将从全局视角出发,拆解AQS的源码脉络,分析其核心机制,包括独占模式、共享模式和条件队列的实现逻辑。随后,通过模拟面试场景,层层提问,检验对AQS的理解。文章结构清晰,先提供鸟瞰图,再细化分析,最后以面试形式加深理解。
一、AQS全局鸟瞰
AbstractQueuedSynchronizer
(AQS)是Java并发包(java.util.concurrent.locks
)中的核心抽象类,提供了基于FIFO等待队列的同步器框架,用于实现锁、信号量、事件等并发工具。AQS通过一个int
类型的状态变量(state
)和CLH队列管理线程的同步行为,支持独占模式和共享模式。
1.1 核心组件与结构
AQS的实现依赖以下关键部分:
-
状态变量(state) :
volatile int
类型,表示同步状态,含义由子类定义。例如:- 在
ReentrantLock
中,state == 0
表示锁未被占用,state > 0
表示锁被持有,数值表示重入次数。 - 在
CountDownLatch
中,state
表示剩余的计数。
- 在
-
CLH队列:基于Craig, Landin, and Hagersten锁的变体,维护等待线程的FIFO队列:
- 队列由
Node
节点组成,每个节点代表一个等待线程。 - 使用
head
和tail
指针管理队列,节点通过prev
和next
双向链接。
- 队列由
-
独占模式与共享模式:
- 独占模式:一次只有一个线程可以获取同步器(如
ReentrantLock
)。 - 共享模式:多个线程可以同时获取同步器(如
Semaphore
)。
- 独占模式:一次只有一个线程可以获取同步器(如
-
条件队列:通过
ConditionObject
支持条件等待(await
/signal
),类似于Object
的wait
/notify
。 -
核心方法:
- 获取:
acquire
(独占)、acquireShared
(共享)。 - 释放:
release
(独占)、releaseShared
(共享)。 - 子类实现:
tryAcquire
、tryRelease
、tryAcquireShared
、tryReleaseShared
、isHeldExclusively
。
- 获取:
1.2 工作原理概览
AQS通过state
和CLH队列管理线程的同步:
-
获取同步器:
- 线程调用
acquire
或acquireShared
,尝试通过tryAcquire
或tryAcquireShared
更新state
。 - 若失败,线程进入CLH队列,阻塞等待。
- 线程调用
-
释放同步器:
- 线程调用
release
或releaseShared
,通过tryRelease
或tryReleaseShared
更新state
。 - 若释放成功,唤醒队列中的下一个线程。
- 线程调用
-
条件等待:
- 线程在条件队列中等待(
await
),通过signal
转移到CLH队列重新竞争。
- 线程在条件队列中等待(
1.3 设计理念
- 模板方法模式:AQS定义了通用的队列和阻塞逻辑,子类通过实现
try*
方法定制状态管理。 - 非公平性默认:AQS允许“插队”(barging),新线程可能优先获取同步器,子类可通过
hasQueuedPredecessors
实现公平性。 - 高性能:使用CAS(
compareAndSetState
)和LockSupport
(park
/unpark
)实现高效的线程管理。
二、AQS源码拆解
以下按照逻辑脉络拆解AQS的核心源码,重点分析其实现细节。
2.1 类结构与核心字段
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
private transient volatile Node head;
private transient volatile Node tail;
private volatile int state;
}
state
:同步状态,volatile
保证可见性,通过getState
、setState
、compareAndSetState
操作。head
和tail
:CLH队列的头尾指针,volatile
确保线程安全。- 继承
AbstractOwnableSynchronizer
:提供exclusiveOwnerThread
记录独占线程。
2.2 Node类与CLH队列
abstract static class Node {
volatile Node prev;
volatile Node next;
Thread waiter;
volatile int status;
}
static final class ExclusiveNode extends Node { }
static final class SharedNode extends Node { }
static final class ConditionNode extends Node implements ForkJoinPool.ManagedBlocker { ... }
-
Node结构:
prev
和next
:双向链接,构成CLH队列。waiter
:记录等待线程。status
:节点状态,包括WAITING
(等待)、CANCELLED
(取消)、COND
(条件等待)。
-
子类:
ExclusiveNode
:独占模式节点。SharedNode
:共享模式节点。ConditionNode
:条件队列节点,支持await
/signal
。
-
队列操作:
enqueue
:通过CAS将节点加入tail
。signalNext
:唤醒队列中的下一个节点。
2.3 核心获取逻辑
public final void acquire(int arg) {
if (!tryAcquire(arg))
acquire(null, arg, false, false, false, 0L);
}
acquire
:独占模式获取,调用子类的tryAcquire
,失败则进入acquire
方法。- 核心方法
acquire(Node, int, boolean, boolean, boolean, long)
:
final int acquire(Node node, int arg, boolean shared,
boolean interruptible, boolean timed, long time) {
Thread current = Thread.currentThread();
byte spins = 0, postSpins = 0;
boolean interrupted = false, first = false;
Node pred = null;
for (;;) {
if (!first && (pred = (node == null) ? null : node.prev) != null &&
!(first = (head == pred))) {
if (pred.status < 0) {
cleanQueue();
continue;
} else if (pred.prev == null) {
Thread.onSpinWait();
continue;
}
}
if (first || pred == null) {
boolean acquired;
try {
if (shared)
acquired = (tryAcquireShared(arg) >= 0);
else
acquired = tryAcquire(arg);
} catch (Throwable ex) {
cancelAcquire(node, interrupted, false);
throw ex;
}
if (acquired) {
if (first) {
node.prev = null;
head = node;
pred.next = null;
node.waiter = null;
if (shared)
signalNextIfShared(node);
if (interrupted)
current.interrupt();
}
return 1;
}
}
if (node == null) {
if (shared)
node = new SharedNode();
else
node = new ExclusiveNode();
} else if (pred == null) {
node.waiter = current;
Node t = tail;
node.setPrevRelaxed(t);
if (t == null)
tryInitializeHead();
else if (!casTail(t, node))
node.setPrevRelaxed(null);
else
t.next = node;
} else if (first && spins != 0) {
--spins;
Thread.onSpinWait();
} else if (node.status == 0) {
node.status = WAITING;
} else {
long nanos;
spins = postSpins = (byte)((postSpins << 1) | 1);
if (!timed)
LockSupport.park(this);
else if ((nanos = time - System.nanoTime()) > 0L)
LockSupport.parkNanos(this, nanos);
else
break;
node.clearStatus();
if ((interrupted |= Thread.interrupted()) && interruptible)
break;
}
}
return cancelAcquire(node, interrupted, interruptible);
}
-
逻辑流程:
- 检查是否为队列头部,若是则尝试
tryAcquire
或tryAcquireShared
。 - 若获取失败,创建节点并尝试入队(
casTail
)。 - 入队后设置
WAITING
状态,调用LockSupport.park
阻塞。 - 被唤醒后重试获取,处理中断或超时。
- 获取成功,更新
head
,清除节点引用。
- 检查是否为队列头部,若是则尝试
-
关键点:
- 使用CAS确保入队原子性。
- 支持中断(
interruptible
)和超时(timed
)。 - 共享模式下,成功获取后唤醒后续共享节点(
signalNextIfShared
)。
2.4 核心释放逻辑
public final boolean release(int arg) {
if (tryRelease(arg)) {
signalNext(head);
return true;
}
return false;
}
release
:独占模式释放,调用子类的tryRelease
,成功则唤醒下一个节点(signalNext
)。- signalNext:
private static void signalNext(Node h) {
Node s;
if (h != null && (s = h.next) != null && s.status != 0) {
s.getAndUnsetStatus(WAITING);
LockSupport.unpark(s.waiter);
}
}
- 清除
WAITING
状态,调用LockSupport.unpark
唤醒线程。
2.5 条件队列(ConditionObject)
public class ConditionObject implements Condition, java.io.Serializable {
private transient ConditionNode firstWaiter;
private transient ConditionNode lastWaiter;
public final void signal() {
ConditionNode first = firstWaiter;
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
if (first != null)
doSignal(first, false);
}
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
ConditionNode node = new ConditionNode();
int savedState = enableWait(node);
LockSupport.setCurrentBlocker(this);
boolean interrupted = false, cancelled = false, rejected = false;
while (!canReacquire(node)) {
if (interrupted |= Thread.interrupted()) {
if (cancelled = (node.getAndUnsetStatus(COND) & COND) != 0)
break;
} else if ((node.status & COND) != 0) {
try {
if (rejected)
node.block();
else
ForkJoinPool.managedBlock(node);
} catch (RejectedExecutionException ex) {
rejected = true;
} catch (InterruptedException ie) {
interrupted = true;
}
} else
Thread.onSpinWait();
}
LockSupport.setCurrentBlocker(null);
node.clearStatus();
acquire(node, savedState, false, false, false, 0L);
if (interrupted) {
if (cancelled) {
unlinkCancelledWaiters(node);
throw new InterruptedException();
}
Thread.currentThread().interrupt();
}
}
}
-
结构:
ConditionNode
组成单向链表(nextWaiter
),维护条件等待队列。 -
signal:将
firstWaiter
转移到CLH队列(enqueue
),唤醒线程。 -
await:
- 创建
ConditionNode
,加入条件队列。 - 释放锁(
release
),保存state
。 - 阻塞(
LockSupport.park
或ForkJoinPool.managedBlock
)。 - 被
signal
唤醒后,转移到CLH队列,重新acquire
锁。
- 创建
-
关键点:
- 条件队列与CLH队列分离,仅在独占模式下使用。
- 支持中断和超时处理。
2.6 队列管理
- 入队(enqueue) :
final void enqueue(Node node) {
if (node != null) {
for (;;) {
Node t = tail;
node.setPrevRelaxed(t);
if (t == null)
tryInitializeHead();
else if (casTail(t, node)) {
t.next = node;
if (t.status < 0)
LockSupport.unpark(node.waiter);
break;
}
}
}
}
- 使用CAS更新
tail
,确保原子性。 - 懒初始化
head
和tail
,减少无竞争时的开销。 - 清理(cleanQueue) :
private void cleanQueue() {
for (;;) {
for (Node q = tail, s = null, p, n;;) {
if (q == null || (p = q.prev) == null)
return;
if (s == null ? tail != q : (s.prev != q || s.status < 0))
break;
if (q.status < 0) {
if ((s == null ? casTail(q, p) : s.casPrev(q, p)) &&
q.prev == p) {
p.casNext(q, s);
if (p.prev == null)
signalNext(p);
}
break;
}
if ((n = p.next) != q) {
if (n != null && q.prev == p) {
p.casNext(n, q);
if (p.prev == null)
signalNext(p);
}
break;
}
s = q;
q = q.prev;
}
}
}
- 移除取消节点(
status == CANCELLED
),修复队列链接。
三、AQS生命周期分析
以下分析AQS在独占模式和共享模式下的生命周期,以ReentrantLock
(独占)和Semaphore
(共享)为例。
3.1 独占模式(以ReentrantLock为例)
-
获取锁:
- 线程调用
lock()
,触发tryAcquire
(检查state == 0
,CAS设置为1)。 - 若成功,设置
exclusiveOwnerThread
,返回。 - 若失败,创建
ExclusiveNode
,入队,调用LockSupport.park
阻塞。
- 线程调用
-
释放锁:
- 线程调用
unlock()
,触发tryRelease
(state--
,若state == 0
,清除exclusiveOwnerThread
)。 - 若释放成功,调用
signalNext
唤醒队列头部线程。
- 线程调用
-
条件等待:
- 线程调用
Condition.await()
,创建ConditionNode
,加入条件队列,释放锁。 - 调用
signal()
,将ConditionNode
转移到CLH队列,线程重新竞争锁。
- 线程调用
3.2 共享模式(以Semaphore为例)
-
获取许可:
- 线程调用
acquire()
,触发tryAcquireShared
(检查state >= permits
,CAS减少state
)。 - 若成功,返回。
- 若失败,创建
SharedNode
,入队,阻塞。
- 线程调用
-
释放许可:
- 线程调用
release()
,触发tryReleaseShared
(CAS增加state
)。 - 若成功,调用
signalNextIfShared
,唤醒后续共享节点。
- 线程调用
-
传播机制:
- 共享模式下,一个线程获取成功后,通知后续共享节点尝试获取,允许多线程同时通过。
3.3 关键点
- 独占模式:严格互斥,队列头部线程独占同步器。
- 共享模式:允许多线程同时获取,传播机制提高吞吐量。
- 条件队列:仅在独占模式下使用,与CLH队列动态切换。
四、模拟面试:层层拷打
以下模拟面试官对AQS的提问,从基础到深入,检验理解深度。
4.1 基础问题
Q1:AQS是什么?它的核心作用是什么?
- A:AQS(
AbstractQueuedSynchronizer
)是Java并发包中的同步器框架,基于state
和CLH队列实现阻塞锁和同步工具(如ReentrantLock
、Semaphore
)。它通过模板方法模式,定义通用逻辑,子类实现具体状态管理。
Q2:AQS支持哪些模式?如何区分独占和共享模式?
-
A:
- 独占模式:一次只有一个线程获取(如
ReentrantLock
),通过tryAcquire
/tryRelease
。 - 共享模式:允许多线程获取(如
Semaphore
),通过tryAcquireShared
/tryReleaseShared
。 - 区分在于
tryAcquireShared
返回int
(负数失败,0成功但不传播,正数成功并传播)。
- 独占模式:一次只有一个线程获取(如
4.2 深入问题
Q3:AQS的CLH队列如何实现线程阻塞和唤醒?
-
A:
- 入队:线程通过
casTail
将Node
加入队列尾部,设置WAITING
状态。 - 阻塞:调用
LockSupport.park
,线程挂起。 - 唤醒:释放时调用
signalNext
,通过LockSupport.unpark
唤醒队列头部线程。 - 使用CAS和
volatile
确保线程安全。
- 入队:线程通过
Q4:AQS如何实现公平性?
-
A:
- 默认非公平,允许新线程插队(barging)。
- 公平性通过
hasQueuedPredecessors
实现,子类在tryAcquire
中检查队列,若有前驱线程,返回false
,防止插队。
4.3 刁钻问题
Q5:如果队列中有多个线程等待,释放锁后会发生什么?
-
A:
- 独占模式:
tryRelease
成功后,signalNext
唤醒队列头部线程,尝试tryAcquire
。 - 共享模式:
tryReleaseShared
成功后,signalNextIfShared
唤醒共享节点,多个线程可能同时获取。 - 非公平模式下,新线程可能插队抢占。
- 独占模式:
Q6:AQS的state
溢出怎么办?
-
A:
state
是int
,最大值为Integer.MAX_VALUE
。- 子类需在
tryAcquire
/tryRelease
中检查溢出,抛出IllegalMonitorStateException
或自定义错误。 - 实际场景需合理设计
state
语义,避免溢出。
4.4 场景问题
Q7:调试时发现IllegalMonitorStateException
,可能原因是什么?
-
A:
- 释放锁时,当前线程不是持有者(
tryRelease
或isHeldExclusively
失败)。 - 调用
Condition.signal
/await
时,未持有锁。 - 解决:确保
acquire
和release
成对,检查线程逻辑。
- 释放锁时,当前线程不是持有者(
Q8:如何用AQS实现一个简单的互斥锁?
- A:
class Mutex implements Lock {
private static class Sync extends AbstractQueuedSynchronizer {
protected boolean tryAcquire(int arg) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
protected boolean tryRelease(int arg) {
if (getState() == 0)
throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0);
return true;
}
protected boolean isHeldExclusively() {
return getState() == 1 && getExclusiveOwnerThread() == Thread.currentThread();
}
public Condition newCondition() {
return new ConditionObject();
}
}
private final Sync sync = new Sync();
public void lock() { sync.acquire(1); }
public void unlock() { sync.release(1); }
public Condition newCondition() { return sync.newCondition(); }
public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); }
public boolean tryLock() { return sync.tryAcquire(1); }
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(time));
}
}
tryAcquire
:CAS设置state
为1,记录持有线程。tryRelease
:清空state
和持有线程。- 使用AQS的
acquire
/release
实现锁逻辑。
五、总结
AbstractQueuedSynchronizer
是Java并发编程的基石,通过state
和CLH队列实现了高效的同步机制。核心要点包括:
- 框架设计:模板方法模式,子类定制
try*
方法。 - 队列管理:CLH队列支持独占和共享模式,CAS和
LockSupport
确保高性能。 - 条件队列:支持灵活的等待/通知机制。
- 灵活性:支持公平/非公平、中断、超时等多种场景。
通过源码拆解和模拟面试,我们深入理解了AQS的实现原理和应用场景,为掌握Java并发工具奠定了基础。
希望这篇分析能帮助你彻底理解AQS,在并发编程中游刃有余!