解锁AQS

一、AQS 是什么?

1.1 定义

AbstractQueuedSynchronizer(AQS)是 Java 并发包(java.util.concurrent.locks)中的抽象同步器框架 ,由 Doug Lea 设计,用于构建同步器

java 复制代码
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer {
    
    // 三大核心组件
    private volatile int state;           // 同步状态
    private transient volatile Node head; // 等待队列头
    private transient volatile Node tail; // 等待队列尾
}

1.2 目标

将**"资源状态管理""线程排队/唤醒机制"**解耦:

  • 子类只需定义何时允许线程获取资源(通过重写钩子方法);
  • AQS 负责线程阻塞、入队、唤醒、中断处理等复杂逻辑

1.3 AQS 在 JUC 中的位置

graph TD A[AQS \n AbstractQueuedSynchronizer] --> B[ReentrantLock] A --> C[CountDownLatch] A --> D[Semaphore] A --> E[CyclicBarrier] A --> F[ReentrantReadWriteLock] B --> B1[公平锁/非公平锁] C --> C1[计数器同步] D --> D1[信号量控制] E --> E1[循环屏障] F --> F1[读写分离]

注意:CyclicBarrier不是基于 AQS 实现,而是基于 ReentrantLock + Condition。

二、AQS 核心设计思想

2.1 模板方法模式

AQS 是模板方法模式的经典应用,将同步器的实现分为:

类型 方法 说明
模板方法 acquire(), release(), acquireShared() 等 线程排队、阻塞、唤醒等通用逻辑,不可重写
钩子方法 tryAcquire(), tryRelease(), tryAcquireShared() 等 资源获取/释放的具体语义,必须由子类实现
java 复制代码
// 模板方法(AQS实现固定流程)
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&           // 钩子方法:子类实现
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

// 钩子方法(子类实现具体语义)
protected boolean tryAcquire(int arg) {
    throw new UnsupportedOperationException(); // 必须重写!
}
  • AQS负责:线程队列管理、阻塞唤醒机制
  • 子类负责:资源状态管理、获取释放逻辑

2.2 两种同步模式

特性 独占模式 共享模式
同时获取线程数 1个 多个
节点模式 Node.EXCLUSIVE Node.SHARED
tryAcquire返回值 boolean int
唤醒策略 精确唤醒一个后继 传播唤醒多个后继
典型应用 ReentrantLock CountDownLatch, Semaphore
资源语义 排他性资源 可共享资源

关键区别 :共享模式支持一个释放可能唤醒多个线程


三、AQS 核心数据结构

3.1 同步状态

state字段是AQS的核心状态标识,不同同步器赋予其不同语义:

java 复制代码
public abstract class AbstractQueuedSynchronizer {
    
    private volatile int state;  // volatile保证可见性
    
    // 状态访问方法
    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);
    }
}

不同同步器的state语义

同步器 state含义 数值范围
ReentrantLock 重入次数 0 = 未锁定, ≥1 = 锁定次数
CountDownLatch 剩余计数 N → 0
Semaphore 可用许可数 ≥ 0
ReentrantReadWriteLock 读写状态 高16位 = 读锁, 低16位 = 写锁

3.2 CLH队列节点

AQS 使用双向链表实现的 CLH 队列变体管理等待线程。,队列节点结构如下:

java 复制代码
static final class Node {
    // 节点模式
    static final Node SHARED = new Node();    // 共享模式
    static final Node EXCLUSIVE = null;       // 独占模式
    
    // 等待状态
    static final int CANCELLED =  1;  // 线程已取消(超时/中断)
    static final int SIGNAL    = -1;  // 后继节点需要被唤醒
    static final int CONDITION = -2;  // 在条件队列中等待
    static final int PROPAGATE = -3;  // 共享模式下传播唤醒
    
     // 节点属性
    volatile int waitStatus;    // 等待状态
    volatile Node prev;         // 前驱节点
    volatile Node next;         // 后继节点  
    volatile Thread thread;     // 等待线程
    Node nextWaiter;           // 下一个等待者(条件队列)
}

3.3 等待队列可视化

graph TB %% ========== 样式设置 ========== classDef headNode fill:#e1f5fe,stroke:#01579b,stroke-width:2px classDef signalNode fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px classDef cancelledNode fill:#ffcdd2,stroke:#c62828,stroke-width:2px classDef tailNode fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px classDef threadNode fill:#fff3e0,stroke:#ef6c00,stroke-width:2px classDef stateNode fill:#e8f5e8,stroke:#388e3c,stroke-width:1px class H,T headNode,tailNode class A,B signalNode class C cancelledNode class T1,T2,T3 threadNode class S1,S2,S3,S1_1,S2_1,S3_1 stateNode %% ========== 队列操作说明 ========== subgraph 队列操作流程 O1[1. 新线程入队] --> O2[创建Node节点并添加到队列尾部] O2 --> O3[设置前驱节点状态为SIGNAL] O3 --> O4[线程进入等待状态] O5[2. 节点出队] --> O6[头节点后继获取资源成功] O6 --> O7[该节点成为新头节点] O7 --> O8[原头节点断开连接] end
graph TB %% ========== 主队列结构 ========== subgraph AQS等待队列 direction LR H[虚拟头节点
Head] A[节点A
SIGNAL状态] B[节点B
SIGNAL状态] C[节点C
CANCELLED状态] T[队列尾部
Tail] H -->|next| A A -->|prev| H A -->|next| B B -->|prev| A B -->|next| C C -->|prev| B C -->|next| T T -->|prev| C end %% ========== 线程关联 ========== subgraph 等待线程 T1[Thread-1
运行状态: WAITING] T2[Thread-2
运行状态: WAITING] T3[Thread-3
运行状态: CANCELLED] T1 -->|关联| A T2 -->|关联| B T3 -->|关联| C end

状态说明

状态类型 数值 含义 线程行为
SIGNAL -1 需要唤醒后继节点 线程等待中,需要前驱唤醒
CANCELLED 1 线程已取消获取 线程已中断或超时
CONDITION -2 在条件队列等待 线程在Condition队列
PROPAGATE -3 共享模式传播 共享模式下唤醒传播

队列特点

  • FIFO双向链表:支持高效的节点插入和删除
  • 虚拟头节点:简化边界条件处理
  • 状态驱动:通过节点状态控制线程阻塞和唤醒

3.4 条件队列

java 复制代码
public class ConditionObject implements Condition {
    private transient Node firstWaiter;   // 条件队列头
    private transient Node lastWaiter;    // 条件队列尾
    
    // 条件等待:释放锁,加入条件队列,被唤醒后重新竞争锁
    public final void await() throws InterruptedException {
        if (Thread.interrupted()) throw new InterruptedException();
        Node node = addConditionWaiter();     // 加入条件队列
        int savedState = fullyRelease(node);  // 完全释放锁
        // ... 等待被signal唤醒
    }
}

四、AQS 核心工作流程

4.1 独占模式:获取资源(acquire

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

执行流程

flowchart TD A[调用 acquire] --> B{tryAcquire 成功?} B -- 是 --> C[直接返回] B -- 否 --> D[addWaiter: 创建节点入队] D --> E[acquireQueued: 自旋等待] E --> F{前驱是 head 且 tryAcquire 成功?} F -- 是 --> G[setHead, 返回] F -- 否 --> H{shouldPark?} H -- 是 --> I[park 阻塞] I --> J[被唤醒后继续循环] H -- 否 --> E

4.1.1 步骤1:尝试快速获取(tryAcquire)

java 复制代码
// 由子类实现,如ReentrantLock中的实现
protected boolean tryAcquire(int arg) {
    throw new UnsupportedOperationException();
}

4.1.2 步骤2:创建节点并入队(addWaiter)

java 复制代码
private Node addWaiter(Node mode) {
    Node node = new Node(Thread.currentThread(), mode);
    // 快速路径:直接CAS设置尾节点
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {  // CAS原子操作
            pred.next = node;
            return node;
        }
    }
    // 慢速路径:CAS失败或队列未初始化
    enq(node);
    return node;
}

// 自旋入队,保证节点一定成功加入队列
private Node enq(final Node node) {
    for (;;) {  // 自旋直到成功
        Node t = tail;
        if (t == null) {  // 队列为空,需要初始化
            if (compareAndSetHead(new Node()))  // 设置虚拟头节点
                tail = head;
        } else {
            node.prev = t;
            if (compareAndSetTail(t, node)) {  // CAS设置尾节点
                t.next = node;
                return t;
            }
        }
    }
}

4.1.3 步骤3:队列中自旋获取(acquireQueued)

java 复制代码
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;
            }
            
            // 检查是否需要挂起线程
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())  // 挂起并检查中断
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);  // 取消获取(异常或中断)
    }
}

4.1.4 步骤4:判断是否需要挂起(shouldParkAfterFailedAcquire)

java 复制代码
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    
    if (ws == Node.SIGNAL)  // 前驱节点承诺唤醒我
        return true;
        
    if (ws > 0) {  // 前驱节点已取消,需要跳过这些节点
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        // 设置前驱节点状态为SIGNAL,表示需要它唤醒我
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;  // 第一次返回false,给一次重试机会
}

// 挂起线程并检查中断状态
private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);  // 挂起当前线程
    return Thread.interrupted();  // 返回并清除中断状态
}

4.2 独占模式释放锁(release)

java 复制代码
public final boolean release(int arg) {
    if (tryRelease(arg)) {  // 子类实现释放逻辑
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);  // 唤醒后继节点
        return true;
    }
    return false;
}

// 唤醒后继节点
private void unparkSuccessor(Node node) {
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);  // 清除状态
    
    Node s = node.next;
    // 如果后继节点无效,从尾部向前查找有效节点
    if (s == null || s.waitStatus > 0) {
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        LockSupport.unpark(s.thread);  // 唤醒线程
}

4.3 共享模式核心方法

4.3.1 共享获取(acquireShared)

java 复制代码
public final void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)  // 返回值语义:
        doAcquireShared(arg);        // <0:失败, 0:成功无剩余, >0:成功有剩余
}

private void doAcquireShared(int arg) {
    final Node node = addWaiter(Node.SHARED);  // 共享模式节点
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            if (p == head) {
                int r = tryAcquireShared(arg);
                if (r >= 0) {  // 获取成功
                    setHeadAndPropagate(node, r);  // 设置头节点并传播
                    p.next = null;
                    if (interrupted) selfInterrupt();
                    failed = false;
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed) cancelAcquire(node);
    }
}

4.3.2 共享释放(releaseShared)

java 复制代码
public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();  // 共享模式的关键:传播唤醒
        return true;
    }
    return false;
}

// 共享模式的关键:传播唤醒机制
private void doReleaseShared() {
    for (;;) {
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;  // CAS失败,重试
                unparkSuccessor(h);  // 唤醒一个后继
            }
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;  // 设置传播状态,确保唤醒继续传播
        }
        if (h == head)  // 如果头节点没变,说明没有竞争,退出
            break;
    }
}

五、典型同步器的 AQS 实现分析

5.1 ReentrantLock(独占 + 可重入)

核心逻辑

  • state = 0:未锁定
  • state > 0:已锁定,值 = 重入次数
  • 持有线程记录在 exclusiveOwnerThread
java 复制代码
// 非公平锁 tryAcquire
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    } else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

公平 vs 非公平

类型 获取锁前是否检查队列
公平锁 是(hasQueuedPredecessors()
非公平锁 否(直接尝试 CAS)

非公平锁性能更高(减少上下文切换),但可能导致线程饥饿。

5.2 CountDownLatch(共享模式)

核心逻辑

  • state = N(初始计数)
  • await()→ acquireShared(1)
  • countDown()→ releaseShared(1)
java 复制代码
protected int tryAcquireShared(int acquires) {
    return (getState() == 0) ? 1 : -1; // 0 表示门已开
}

protected boolean tryReleaseShared(int releases) {
    for (;;) {
        int c = getState();
        if (c == 0) return false;
        int nextc = c - 1;
        if (compareAndSetState(c, nextc))
            return nextc == 0; // 归零则需唤醒所有等待者
    }
}

一次性:state 不能重置,故不可重用。

5.3 Semaphore(共享模式)

核心逻辑

  • state = permits(许可数)
  • acquire() → 尝试减少许可
  • release() → 增加许可
java 复制代码
protected int tryAcquireShared(int acquires) {
    for (;;) {
        int available = getState();
        int remaining = available - acquires;
        if (remaining < 0 || compareAndSetState(available, remaining))
            return remaining;
    }
}

六、基于AQS实现自定义同步器

6.1 简单互斥锁实现

java 复制代码
/**
 * 基于AQS实现的简单不可重入互斥锁
 */
public class SimpleMutex extends AbstractQueuedSynchronizer {
    
    // 尝试获取锁:state从0变为1
    @Override
    protected boolean tryAcquire(int acquires) {
        assert acquires == 1; 
        if (compareAndSetState(0, 1)) {  // CAS操作保证原子性
            setExclusiveOwnerThread(Thread.currentThread());  // 设置独占线程
            return true;
        }
        return false;
    }
    
    // 释放锁:state从1变为0
    @Override
    protected boolean tryRelease(int releases) {
        assert releases == 1;
        if (getState() == 0) 
            throw new IllegalMonitorStateException("未持有锁");
        setExclusiveOwnerThread(null);  // 清除独占线程
        setState(0);  // 不需要CAS,因为只有持有者能释放
        return true;
    }
    
    // 是否被当前线程独占
    @Override
    protected boolean isHeldExclusively() {
        return getState() == 1 && 
               getExclusiveOwnerThread() == Thread.currentThread();
    }
    
    // 对外提供的API
    public void lock() { acquire(1); }
    public boolean tryLock() { return tryAcquire(1); }
    public void unlock() { release(1); }
    public boolean isLocked() { return getState() == 1; }
    
    // 使用示例
    public static void main(String[] args) {
        SimpleMutex lock = new SimpleMutex();
        
        // 线程1
        new Thread(() -> {
            lock.lock();
            try {
                System.out.println("Thread 1 acquired lock");
                Thread.sleep(1000);
                System.out.println("Thread 1 releasing lock");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                lock.unlock();
            }
        }).start();
        
        // 线程2  
        new Thread(() -> {
            lock.lock();
            try {
                System.out.println("Thread 2 acquired lock");
            } finally {
                lock.unlock();
            }
        }).start();
    }
}

6.2 简单CountDownLatch实现

java 复制代码
/**
 * 基于AQS的CountDownLatch实现
 */
public class SimpleCountDownLatch {
    private final Sync sync;
    
    private static final class Sync extends AbstractQueuedSynchronizer {
        Sync(int count) {
            setState(count);  // 初始化计数器
        }
        
        int getCount() {
            return getState();
        }
        
        // 共享获取:计数器为0时才能成功
        @Override
        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }
        
        // 共享释放:计数器减1
        @Override
        protected boolean tryReleaseShared(int releases) {
            for (;;) {  // 循环CAS直到成功
                int current = getState();
                if (current == 0) return false;  // 已经为0
                int next = current - 1;
                if (compareAndSetState(current, next)) {
                    return next == 0;  // 返回true表示需要唤醒等待线程
                }
            }
        }
    }
    
    public SimpleCountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }
    
    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
    
    public void countDown() {
        sync.releaseShared(1);
    }
    
    public long getCount() {
        return sync.getCount();
    }
}

6.3 公平锁 vs 非公平锁实现

java 复制代码
// 非公平锁:直接尝试获取,不管等待队列
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (compareAndSetState(0, acquires)) {  // 不管队列,直接抢
            setExclusiveOwnerThread(current);
            return true;
        }
    } else if (current == getExclusiveOwnerThread()) {  // 重入逻辑
        int nextc = c + acquires;
        if (nextc < 0) throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

// 公平锁:检查队列中是否有等待者
protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 关键区别:检查是否有前驱节点在等待
        if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    } else if (current == getExclusiveOwnerThread()) {  // 重入逻辑
        int nextc = c + acquires;
        if (nextc < 0) throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

七、总结

最近2个月陆陆续续的面试了不少公司,发现自己在许多方面还是有不足,在面试过程中查缺补漏,对一些问题会自己写成个人笔记,这篇关于AQS的就是前阵子回顾时,写好的,如有不足之处,还请见谅。

相关推荐
少妇的美梦4 小时前
Kubernetes(K8s)YAML 配置文件编写教程
运维·后端
沐怡旸4 小时前
【底层机制】【Android】本地Socket 对比 Binder 以及在 Android系统中的应用
android·面试
gAlAxy...4 小时前
面试(六)——Java IO 流
java·面试·职场和发展
狂团商城小师妹4 小时前
JAVA无人共享台球杆台球柜系统球杆柜租赁系统源码支持微信小程序
java·开发语言·微信小程序·小程序
麦麦鸡腿堡4 小时前
Java的抽象类实践-模板设计模式
java·开发语言·设计模式
武子康4 小时前
大数据-134 ClickHouse 单机+集群节点落地手册 | 安装配置 | systemd 管理 / config.d
大数据·分布式·后端
沐怡旸4 小时前
【底层机制】【Android】Binder 驱动作用
android·面试
沐怡旸4 小时前
【底层机制】【Android】详解 Zygote
android·面试
Tech有道4 小时前
美团面试题:"TRUNCATE vs DELETE:这道面试题你答对了吗?深入解析背后的差异"
后端·面试