文章中如果有图看不到,可以点这里去 csdn 看看。从那边导过来的,文章太多,没法一篇篇修改好。

Java 锁相关详解【六、Java AQS(AbstractQueuedSynchronizer)源码深度解析】

Java AQS(AbstractQueuedSynchronizer) 源码深度解析

一、AQS 简介

AbstractQueuedSynchronizer(简称 AQS)是 JUC 并发框架的核心基础类,几乎所有常见的并发工具(ReentrantLockSemaphoreCountDownLatchCyclicBarrierReentrantReadWriteLock 等)都基于 AQS 构建。

它的核心思想是:

  • 使用一个整数 state 表示同步状态(0/1 表示锁是否被占用,或表示剩余资源数量)。
  • 通过 FIFO 队列(CLH 队列)管理等待线程
  • 提供模板方法,子类只需重写部分方法 即可实现不同的同步器。

AQS 提供了两种模式:

  1. 独占模式(Exclusive):同一时刻只有一个线程能持有资源(如 ReentrantLock)。
  2. 共享模式(Shared):多个线程可同时访问资源(如 Semaphore、CountDownLatch)。

二、核心数据结构

2.1 state

AQS 内部维护一个 volatile int state,用来表示同步状态:

  • 在独占锁中,state=0 表示未加锁,1 表示加锁。
  • 在可重入锁中,state 记录加锁次数。
  • 在共享锁中,state 可表示剩余许可数量。

更新方式:

  • compareAndSetState(int expect, int update)
  • getState() / setState()

依赖 CAS 保证线程安全。


2.2 CLH 队列

AQS 使用 变种 CLH(Craig, Landin, and Hagersten)队列 来管理等待线程。

队列节点:

static final class Node {
    volatile Node prev;    // 前驱节点
    volatile Node next;    // 后继节点
    volatile Thread thread; // 当前节点代表的线程
    volatile int waitStatus; // 节点状态
}

节点状态(waitStatus):

  • 0:默认状态
  • SIGNAL=-1:表示后继线程需要被唤醒
  • CANCELLED=1:线程取消等待
  • CONDITION=-2:表示节点在条件队列中
  • PROPAGATE=-3:共享模式下的传播标识

2.3 模板方法

AQS 提供一系列需要子类实现或重写的模板方法:

  • 独占模式

    • tryAcquire(int arg)
    • tryRelease(int arg)
  • 共享模式

    • tryAcquireShared(int arg)
    • tryReleaseShared(int arg)
  • 条件队列支持

    • isHeldExclusively()

子类只需实现这些方法,线程排队/阻塞/唤醒的逻辑由 AQS 完成。


三、独占模式源码解析

ReentrantLock 为例,内部依赖 AQS 的独占模式。

3.1 获取锁

ReentrantLock.lock() 调用 AQS 的 acquire(int arg)

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

流程:

  1. 尝试直接获取锁(tryAcquire,由子类实现)。
  2. 如果失败,加入等待队列(addWaiter)。
  3. 在队列中自旋等待,直到前驱节点释放锁。

3.2 释放锁

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

流程:

  1. 调用子类实现的 tryRelease,尝试修改 state。
  2. 如果释放成功,唤醒后继节点。

四、共享模式源码解析

Semaphore 为例,内部依赖 AQS 的共享模式。

4.1 获取许可

public void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)
        doAcquireShared(arg);
}
  • tryAcquireShared 返回剩余资源数:

    • >=0:获取成功;
    • <0:获取失败,进入队列。

4.2 释放许可

public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}

doReleaseShared 会唤醒后继节点,传播共享信号。


五、条件队列(Condition)

AQS 支持 ConditionObject,实现了 await/signal 机制:

  • await():当前线程释放锁,进入条件队列,等待被唤醒。
  • signal():将条件队列中的一个节点转移到同步队列,准备竞争锁。

这是 synchronized + Object.wait/notify 的显式锁版本。


六、整体流程图

可以用一个 线程获取独占锁的流程图(Mermaid 表示):

线程调用 acquire
tryAcquire 成功?
获取锁成功
加入 CLH 队列
自旋等待前驱节点释放

七、工程实践与建议

  1. 尽量使用现成的并发工具类

    • 不要直接继承 AQS 除非你需要实现新的同步器。

    • 例如:

      • ReentrantLock(独占锁)
      • Semaphore(限流器)
      • CountDownLatch(倒计时器)
      • ReentrantReadWriteLock(读写锁)
  2. 理解 AQS,有助于调优

    • ReentrantLock 的公平/非公平模式本质就是 AQS 入队策略的不同。
    • Semaphore 的许可数量就是 AQS 的 state。
  3. 避免错误使用

    • 不要直接操作 AQS 的队列或 state,必须通过模板方法。
    • 子类实现需严格保证 state 的正确性

八、总结

  • AQS 是 JUC 并发工具的核心基石,通过 state + CLH 队列实现线程同步。
  • 独占模式:支持互斥锁,如 ReentrantLock。
  • 共享模式:支持多线程共享资源,如 Semaphore、CountDownLatch。
  • ConditionObject:支持条件队列,实现等待/通知机制。
  • 工程实践:优先使用 JUC 提供的工具类,理解 AQS 原理有助于调试与优化。

一句话总结:
掌握 AQS,就等于掌握了 Java 并发框架的心脏。

posted @ 2025-09-04 16:57  NeoLshu  阅读(4)  评论(0)    收藏  举报  来源