一句话说透Java里面的AbstractQueuedSynchronizer

124 阅读3分钟

AQS(抽象队列同步器)  是 Java 并发包的核心基础框架,用于构建锁和同步工具(如 ReentrantLockSemaphore 等)。它通过 共享状态管理 和 线程排队机制 实现高效的线程同步。以下是其核心要点:


一、AQS 的核心组成

  1. 状态变量(state

    • 一个 volatile int 值,表示同步状态(如锁是否被占用、信号量剩余许可数等)。
    • 子类通过操作 state 定义同步规则(如 ReentrantLock 中 state=0 表示未加锁,state>0 表示锁被持有且可重入次数)。
  2. CLH 队列(线程等待队列)

    • 一个双向链表结构的队列,存储等待获取资源的线程。
    • 每个节点(Node)包含线程引用、等待状态(如是否被取消)及前后指针。

二、AQS 的工作原理

  1. 获取资源(如加锁)

    • 线程尝试通过 CAS 修改 state,若成功则获取资源。
    • 若失败,将线程包装为 Node 加入 CLH 队列尾部,并阻塞(通过 LockSupport.park())。
  2. 释放资源(如解锁)

    • 修改 state,并唤醒队列中下一个等待的线程(通过 LockSupport.unpark())。

三、AQS 的两种模式

模式特点应用场景
独占模式同一时刻只有一个线程能获取资源(如锁)ReentrantLock
共享模式多个线程可同时获取资源SemaphoreCountDownLatch

四、AQS 的模板方法

子类需实现以下方法定义同步逻辑:

  • 独占模式

    protected boolean tryAcquire(int arg)  // 尝试获取资源
    protected boolean tryRelease(int arg)  // 尝试释放资源
    
  • 共享模式

    protected int tryAcquireShared(int arg)  // 尝试获取共享资源
    protected boolean tryReleaseShared(int arg)  // 尝试释放共享资源
    

示例ReentrantLock 的实现

protected boolean tryAcquire(int arg) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) { // 锁未被占用
        if (compareAndSetState(0, 1)) { // CAS 修改 state
            setExclusiveOwnerThread(current); // 设置当前线程为锁持有者
            return true;
        }
    } else if (current == getExclusiveOwnerThread()) { // 可重入
        setState(c + 1); // 增加重入次数
        return true;
    }
    return false;
}

五、AQS 的等待队列管理

  1. 入队

    • 线程获取资源失败时,创建 Node 并入队,通过自旋 CAS 确保线程安全。
  2. 出队

    • 前驱节点释放资源后,唤醒后续节点尝试获取资源。

六、AQS 的典型应用

同步工具实现原理
ReentrantLock基于独占模式,state 表示锁的持有计数(可重入)。
Semaphore基于共享模式,state 表示剩余许可数,获取许可时减少,释放时增加。
CountDownLatch基于共享模式,state 初始为计数,线程调用 countDown() 减1,减到0时唤醒所有等待线程。

七、AQS 的优缺点

优点缺点
提供通用同步框架,减少重复造轮子需要理解底层原理才能正确扩展
高性能(CAS + CLH 队列优化)调试复杂(涉及线程阻塞与唤醒)

八、总结

AQS 是 Java 并发编程的基石,通过状态管理和线程排队机制,为构建高效、灵活的同步工具提供基础支持。
核心口诀
「AQS 管状态,队列排线程
独占共享两模式,模板方法定规则
ReentrantLock、Semaphore,皆由此来显神通!」