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)

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 节点结构
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;  
}
队列结构
head -> Node1 -> Node2 -> ... -> tail
  • head:头节点(哨兵节点,无实际线程);
  • tail:尾节点(最新加入的节点);
  • 节点通过prevnext形成双向链表。

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

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

获取资源:acquire(int arg)
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 加入队列尾部:

    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:节点在队列中自旋等待,直到获取资源或被取消:

    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)
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 保证状态的线程可见性与原子性。