AQS 深入解析

1. AQS 深入解析

1.1 什么是 AQS

AQS(AbstractQueuedSynchronizer,抽象队列同步器)java.util.concurrent 包的核心基石。

它提供了一套通用的机制来管理同步状态(state)阻塞/唤醒线程管理等待队列

JUC 下的各种锁(ReentrantLockReentrantReadWriteLock)以及并发工具类(SemaphoreCountDownLatchCyclicBarrier 等)都是基于 AQS 实现的。


1.1.1 AQS 的核心思想

  1. CLH 同步队列

    • 全称:Craig, Landin, and Hagersten Queue
    • 用来存储被阻塞的线程 ,是一个 FIFO 双向队列
    • FIFO 公平锁:等待时间最长的线程优先获得锁。
    • 双向队列:支持从两端插入/删除节点(非公平锁可能插队)。
  2. state 属性

    • volatile int state 表示资源状态。
    • 对于 ReentrantLock
      • state=0 → 没有线程持有锁
      • state>0 → 锁被持有(可重入时 state++)
    • 对于 CountDownLatch
      • state=0 → 计数器归零,线程可继续执行
      • state>0 → 继续阻塞

1.2 ReentrantLock 底层原理

sequenceDiagram participant Thread1 as 线程1 participant ReentrantLock participant AQS participant CLHQueue as CLH队列 participant Thread2 as 线程2 Thread1->>ReentrantLock: lock() ReentrantLock->>AQS: tryAcquire() AQS-->>Thread1: 成功获取锁 (state=1) Thread2->>ReentrantLock: lock() ReentrantLock->>AQS: tryAcquire() AQS-->>Thread2: 获取失败 AQS->>CLHQueue: 入队等待 CLHQueue-->>Thread2: 阻塞 (park) Thread1->>ReentrantLock: unlock() ReentrantLock->>AQS: tryRelease() AQS->>CLHQueue: signalNext() CLHQueue-->>Thread2: 唤醒 (unpark) Thread2->>AQS: tryAcquire() AQS-->>Thread2: 成功获取锁 (state=1)

1.2.1 公平锁 vs 非公平锁

  • 公平锁:严格按照线程请求的顺序获取锁。
  • 非公平锁:允许在合适的时机插队(比如锁刚释放时,直接抢占)。

1.2.2 以 ReentrantLock 为例

ReentrantLock 内部有 3 个与 AQS 相关的类:

  1. Sync(抽象类,继承 AQS)
  2. NonfairSync(非公平锁实现)
  3. FairSync(公平锁实现)

上锁流程(非公平锁)

1. lock() 方法
java 复制代码
final void lock() {
    if (!initialTryLock()) // 初次尝试获取锁
        acquire(1);        // 获取失败则进入 AQS 获取流程
}

2. NonfairSync.initialTryLock()
java 复制代码
final boolean initialTryLock() {
    Thread current = Thread.currentThread();
    if (compareAndSetState(0, 1)) { // CAS 将 state 从 0 改为 1
        setExclusiveOwnerThread(current);
        return true;
    } else if (getExclusiveOwnerThread() == current) { // 可重入
        setState(getState() + 1);
        return true;
    }
    return false;
}

3. AQS.acquire()
java 复制代码
public final void acquire(int arg) {
    if (!tryAcquire(arg)) // 再次尝试获取锁
        acquire(null, arg, false, false, false, 0L); // 获取失败则进入队列等待
}

4. acquire(null, arg, false, false, false, 0L) 源码
java 复制代码
final void acquire(Node node, int arg,
                   boolean shared, boolean interruptible,
                   boolean timed, long time) {
    Thread current = Thread.currentThread();
    if (node == null)
        node = addWaiter(shared ? Node.SHARED : Node.EXCLUSIVE);

    boolean failed = true;
    try {
        for (;;) { // 自旋
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) { // 轮到自己了,尝试获取锁
                setHead(node); // 成为新的头节点
                p.next = null; // 帮助 GC
                failed = false;
                return;
            }
            // 阻塞当前线程,等待被唤醒
            if (shouldParkAfterFailedAcquire(p, node))
                parkAndCheckInterrupt();
        }
    } finally {
        if (failed)
            cancelAcquire(node); // 获取失败则取消排队
    }
}

简化理解:

  1. 加到等待队列(如果不是头节点)
  2. 自旋等待 ,直到前驱节点是 head
  3. 尝试获取锁tryAcquire
  4. 获取失败 → 阻塞(park)等待唤醒
  5. 获取成功 → 设为新的 head 节点,退出循环

tryAcquire(非公平锁)

java 复制代码
protected final boolean tryAcquire(int acquires) {
    if (getState() == 0 && compareAndSetState(0, acquires)) {
        setExclusiveOwnerThread(Thread.currentThread());
        return true;
    }
    return false;
}

解锁流程

1. AQS.release()
java 复制代码
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        signalNext(head); // 唤醒下一个等待线程
        return true;
    }
    return false;
}
2. tryRelease()
java 复制代码
protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    if (getExclusiveOwnerThread() != Thread.currentThread())
        throw new IllegalMonitorStateException();
    boolean free = (c == 0);
    if (free)
        setExclusiveOwnerThread(null);
    setState(c);
    return free;
}
3. signalNext()
java 复制代码
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);
    }
}

1.3 总结:AQS 的工作原理

  1. state 表示同步状态volatile int,CAS 修改)
  2. CLH 队列管理阻塞线程(FIFO)
  3. 获取锁流程
    • 尝试直接获取锁(tryAcquire
    • 失败 → 加入等待队列 → 自旋等待 → 阻塞
  4. 释放锁流程
    • 修改 state
    • 唤醒队列中下一个等待的线程

💡 一句话概括 AQS

AQS 就是一个基于 CLH 队列的线程等待/唤醒框架,利用 CAS 操作管理 state,让线程安全地获取和释放资源。

相关推荐
@ chen8 分钟前
IDEA初始化配置
java·ide·intellij-idea
wellc1 小时前
SpringBoot集成Flowable
java·spring boot·后端
Hui Baby2 小时前
springAi+MCP三种
java
hsjcjh2 小时前
【MySQL】C# 连接MySQL
java
敖正炀2 小时前
LinkedBlockingDeque详解
java
wangyadong3172 小时前
datagrip 链接mysql 报错
java
untE EADO2 小时前
Tomcat的server.xml配置详解
xml·java·tomcat
ictI CABL2 小时前
Tomcat 乱码问题彻底解决
java·tomcat
敖正炀2 小时前
DelayQueue 详解
java
敖正炀2 小时前
PriorityBlockingQueue 详解
java