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,让线程安全地获取和释放资源。

相关推荐
我崽不熬夜32 分钟前
掌握Java中的数组与集合:如何灵活处理不同的数据结构?
java·后端·java ee
_码农121381 小时前
模拟tomcat接收GET、POST请求
java·tomcat
板板正2 小时前
SpringAI——向量存储(vector store)
java·spring boot·ai
野生技术架构师2 小时前
Spring Boot 定时任务与 xxl-job 灵活切换方案
java·spring boot·后端
苹果醋33 小时前
Java并发编程-Java内存模型(JMM)
java·运维·spring boot·mysql·nginx
你怎么知道我是队长3 小时前
C语言---编译的最小单位---令牌(Token)
java·c语言·前端
Elieal4 小时前
Java 链表完全指南:从基础到力扣简单题实战
java·leetcode·链表
寒士obj4 小时前
SpringBoot中的条件注解
java·spring boot·后端
pengzhuofan4 小时前
Java设计模式-外观模式
java·设计模式·外观模式