AbstractQueuedSynchronizer
(简称 AQS)是 Java 并发包 java.util.concurrent.locks
中的一个核心同步框架,用于构建锁和同步器,如:
AQS 通过一个FIFO 双向等待队列(CLH 队列)管理线程的同步状态,使开发者可以专注于同步逻辑,而不必关注线程的调度、阻塞、唤醒等底层细节。
private volatile int state; // 同步状态
private transient Node head; // 队列头节点
private transient Node tail; // 队列尾节点
state
:用来表示资源的占用状态(例如是否被锁定、可用信号量数等);head
和 tail
:维护一个 CLH 等待队列,用于管理阻塞的线程。AQS 提供一系列模板方法用于子类实现:
// 共享模式
protected int tryAcquireShared(int arg);
protected boolean tryReleaseShared(int arg);
// 独占模式
protected boolean tryAcquire(int arg);
protected boolean tryRelease(int arg);
子类需要实现这些方法,以控制对资源的获取与释放逻辑。
lock.lock() → tryAcquire() 尝试获取 → 获取失败 → 加入 CLH 队列 → park(阻塞)→ 被唤醒时再次尝试
流程:
tryAcquire()
尝试获取资源;Node
加入等待队列;unlock() → tryRelease() 成功 → 唤醒队列中下一个线程
tryRelease()
将 state
设置为 0;unparkSuccessor()
唤醒下一个线程。模式 | 描述 |
---|---|
独占模式 | 同一时刻只能有一个线程访问,如 ReentrantLock |
共享模式 | 多个线程可以共享资源,如 Semaphore、CountDownLatch |
AQS 区分这两种模式,并分别处理入队、出队、唤醒等逻辑。
AQS 使用一种变体的 CLH(Craig–Landin–Hagersten)同步队列 实现线程排队。
head -> Node1 -> Node2 -> ... -> tail
class MyLock extends AbstractQueuedSynchronizer {
protected boolean tryAcquire(int arg) {
return compareAndSetState(0, 1);
}
protected boolean tryRelease(int arg) {
setState(0);
return true;
}
public void lock() {
acquire(1); // 内部调用 tryAcquire + 入队逻辑
}
public void unlock() {
release(1);
}
}
countDown()
执行 releaseShared(-1)
;await()
会在 state = 0 前阻塞。优点 | 说明 |
---|---|
封装阻塞逻辑 | 使用 LockSupport 封装了 park/unpark |
可复用性强 | 模板方法模式,便于自定义同步器 |
队列高效 | FIFO 队列实现公平性,性能稳定 |
支持两种模式 | 支持共享和独占资源控制 |
try...finally
中释放锁;unpark
会导致阻塞线程永久等待;StampedLock
或乐观锁优化。类名 | 模式 | 简介 |
---|---|---|
ReentrantLock | 独占 | 可重入、可中断、公平/非公平 |
Semaphore | 共享 | 控制资源访问数目 |
CountDownLatch | 共享 | 等待所有任务完成 |
ReentrantReadWriteLock | 共享 + 独占 | 高效读写分离 |
FutureTask | 独占 | 控制异步任务状态 |
AbstractQueuedSynchronizer | 基类 | 框架级同步器 |
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
acquire()
│
┌───────┴────────┐
↓ ↓
tryAcquire() // 自定义尝试获取
(成功返回true) (失败)
↓ ↓
return addWaiter(Node.EXCLUSIVE)
↓
入队列构造双向链表
↓
acquireQueued(node, arg)
↓
while(前驱不是head || 不能获取锁)
↓ ↓
park() tryAcquire()
↓
获取成功 → 设置head → unpark下一个
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
release()
│
tryRelease()
│
是否成功释放?
↓ ↑
true false
↓
获取 head
↓
唤醒下一个节点(unpark)
public class ReentrantLock implements Lock {
abstract static class Sync extends AbstractQueuedSynchronizer {
// 核心逻辑都在此子类中
}
final Sync sync;
}
类型 | 类名 | 特点 |
---|---|---|
非公平锁(默认) | NonfairSync |
直接尝试获取锁,抢占式,性能好 |
公平锁 | FairSync |
排队获取,按顺序,不插队,公平但慢一些 |
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 尝试直接 CAS 获取锁
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
} else if (current == getExclusiveOwnerThread()) {
// 可重入,加重入次数
int nextc = c + acquires;
setState(nextc);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
AQS 底层通过 LockSupport.park()
和 unpark(thread)
来挂起/唤醒线程:
park()
:当前线程阻塞,等待被唤醒;unpark(Thread t)
:唤醒指定线程;wait/notify
,更灵活、底层、性能好。特性 | 说明 |
---|---|
模板方法设计 | 只需实现 tryAcquire 和 tryRelease ,其他线程队列处理由 AQS 自动完成 |
双模式支持 | 支持共享与独占两种访问模型 |
高可复用性 | 可构建多种同步组件(锁、信号量、栅栏等) |
高性能 | 结合 CAS、自旋、队列挂起唤醒机制 |
阻塞线程管理 | 使用 LockSupport 精细控制线程挂起与恢复 |