0%

AQS

AQS(AbstractQueuedSynchronizer):Java 同步框架的基石

AQS(AbstractQueuedSynchronizer,队列同步器)是 Java 并发包(JUC)的核心基础组件,为 ReentrantLock、Semaphore、CountDownLatch 等同步工具提供统一的底层支持。它通过同步状态管理FIFO 等待队列实现了高效的线程同步机制。本文将深入解析 AQS 的设计原理、核心组件及工作流程。

AQS 的核心设计思想

AQS 的核心目标是简化同步组件的实现,其设计基于模板方法模式

  • 子类通过继承 AQS 并实现特定抽象方法(如tryAcquiretryRelease)来定义同步状态的获取与释放规则;
  • AQS 自身实现了队列管理、线程阻塞 / 唤醒等通用逻辑,无需子类重复开发。

核心组件

  • 同步状态(state):一个volatile int变量,用于表示资源的占有状态(如 0 表示未占用,1 表示已占用);
  • FIFO 等待队列:双向链表结构,存储等待获取资源的线程节点;
  • 条件队列(Condition):与Lock配合使用,实现线程间的条件等待。

AQS 核心源码解析

AQS的子类使用

同步状态(state)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements Serializable {  
// 同步状态,volatile保证可见性
private volatile int state;

// 获取当前状态
protected final int getState() { return state; }

// 设置状态(无原子性保证)
protected final void setState(int newState) { state = newState; }

// CAS设置状态(保证原子性)
protected final boolean compareAndSetState(int expect, int update) {
return UNSAFE.compareAndSwapInt(this, stateOffset, expect, update);
}
}

状态的意义

  • 对于ReentrantLockstate表示重入次数(0:未锁定;n>0:被锁定 n 次);
  • 对于Semaphorestate表示剩余许可数;
  • 对于CountDownLatchstate表示计数器值。

FIFO 等待队列(同步队列)

等待队列是 AQS 的核心,用于存储因获取资源失败而阻塞的线程。队列由Node节点组成,结构如下:

Node 节点结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static final class Node {  
// 共享模式标记(用于Semaphore等)
static final Node SHARED = new Node();
// 独占模式标记(用于ReentrantLock等)
static final Node EXCLUSIVE = null;

// 节点状态(等待状态)
volatile int waitStatus;
static final int CANCELLED = 1; // 已取消(超时或中断)
static final int SIGNAL = -1; // 需唤醒后继节点
static final int CONDITION = -2; // 在条件队列中
static final int PROPAGATE = -3; // 共享模式下的传播

// 前驱节点
volatile Node prev;
// 后继节点
volatile Node next;
// 节点关联的线程
volatile Thread thread;
// 条件队列中的后继节点(或共享模式标记)
Node nextWaiter;
}
队列结构
1
head -> Node1 -> Node2 -> ... -> tail  
  • head:头节点(哨兵节点,无实际线程);
  • tail:尾节点(最新加入的节点);
  • 节点通过prevnext形成双向链表。

独占模式下的资源获取与释放

ReentrantLock的独占锁为例,解析 AQS 的核心流程。

获取资源:acquire(int arg)
1
2
3
4
5
6
7
8
public final void acquire(int arg) {  
// 1. 尝试获取资源(tryAcquire由子类实现)
// 2. 失败则加入等待队列(addWaiter)
// 3. 在队列中阻塞等待(acquireQueued)
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) {
selfInterrupt(); // 若被中断,恢复中断状态
}
}
  • tryAcquire:子类实现,定义独占模式下的资源获取逻辑(如ReentrantLock判断state是否为 0,是则 CAS 设置为 1)。

  • addWaiter:将当前线程封装为 Node,通过 CAS 加入队列尾部:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    private Node addWaiter(Node mode) {  
    Node node = new Node(Thread.currentThread(), mode);
    Node pred = tail;
    if (pred != null) {
    node.prev = pred;
    if (compareAndSetTail(pred, node)) {
    pred.next = node;
    return node;
    }
    }
    enq(node); // 队列为空或CAS失败时,自旋入队
    return node;
    }
  • acquireQueued:节点在队列中自旋等待,直到获取资源或被取消:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    final boolean acquireQueued(final Node node, int arg) {  
    boolean failed = true;
    try {
    boolean interrupted = false;
    for (;;) {
    final Node p = node.predecessor();
    // 若前驱是头节点,尝试获取资源
    if (p == head && tryAcquire(arg)) {
    setHead(node); // 成为新的头节点
    p.next = null; // 帮助GC
    failed = false;
    return interrupted;
    }
    // 判断是否可以阻塞,若可以则park当前线程
    if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) {
    interrupted = true;
    }
    }
    } finally {
    if (failed) cancelAcquire(node); // 获取失败,取消节点
    }
    }
释放资源:release(int arg)
1
2
3
4
5
6
7
8
9
10
11
public final boolean release(int arg) {  
// 1. 尝试释放资源(tryRelease由子类实现)
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0) {
unparkSuccessor(h); // 2. 唤醒后继节点
}
return true;
}
return false;
}
  • tryRelease:子类实现,定义资源释放逻辑(如ReentrantLockstate减 1,为 0 时表示完全释放)。
  • unparkSuccessor:唤醒后继节点(移除取消节点,调用LockSupport.unpark)。

条件队列(Condition)

AQS 内部类ConditionObject实现了Condition接口,与Lock配合实现线程间的条件等待(类似synchronizedwait/notify)。

核心方法
  • await():将当前线程加入条件队列,释放锁并阻塞;
  • signal():将条件队列的首节点转移到同步队列,等待获取锁;
  • signalAll():转移所有条件队列节点到同步队列。
工作流程
  1. 线程获取锁后,调用await()进入条件队列并释放锁;
  2. 其他线程调用signal(),将条件队列节点转移到同步队列;
  3. 同步队列中的节点竞争锁,获取后继续执行。

LockSupport 工具类

AQS 通过LockSupport实现线程的阻塞与唤醒,其核心方法:

  • park():阻塞当前线程(需unpark唤醒);
  • unpark(Thread t):唤醒指定线程(提前调用可设置 “许可”,使后续park不阻塞)。

LockSupport相比wait/notify的优势:

  • 无需在同步块中调用;
  • 支持中断响应;
  • 避免虚假唤醒(通过 “许可” 机制)。

AQS 的应用:同步组件举例

ReentrantLock(独占锁)

  • tryAcquire:判断state是否为 0,是则 CAS 设置为 1(非公平锁);或检查是否为当前线程,是则state++(重入)。
  • tryReleasestate--,为 0 时释放锁并返回true

Semaphore(信号量)

  • 共享模式:多个线程可同时获取许可。
  • tryAcquireSharedstate减去请求的许可数,若 >=0 则成功。
  • tryReleaseSharedstate加上释放的许可数,唤醒后续节点。

CountDownLatch(倒计时器)

  • tryAcquireShared:若state为 0 则成功,否则失败并加入队列。
  • countDown():调用tryReleaseSharedstate--,为 0 时唤醒所有等待线程。

AQS 的核心优势

  1. 通用性:统一实现队列管理、线程阻塞等逻辑,简化同步组件开发;
  2. 高效性:基于 CAS 和自旋锁,减少锁竞争带来的性能损耗;
  3. 灵活性:支持独占 / 共享模式,可扩展出多种同步工具;
  4. 安全性:通过 volatile 和 CAS 保证状态的线程可见性与原子性。

欢迎关注我的其它发布渠道