解锁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[虚拟头节点<br/>Head] A[节点A<br/>SIGNAL状态] B[节点B<br/>SIGNAL状态] C[节点C<br/>CANCELLED状态] T[队列尾部<br/>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<br/>运行状态: WAITING] T2[Thread-2<br/>运行状态: WAITING] T3[Thread-3<br/>运行状态: 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的就是前阵子回顾时,写好的,如有不足之处,还请见谅。

相关推荐
AskHarries6 小时前
做国内还是出海
后端
J2虾虾6 小时前
Spring AI Alibaba文档
java·人工智能·spring
YikNjy7 小时前
break和continue
java·开发语言·算法
SomeOtherTime7 小时前
Geojson相关(AI回答)
java·前端·python
日月云棠7 小时前
10 Integer —— 最常用的整数包装类深度解析
java·后端
大鸡腿同学7 小时前
大模型为何总 “胡说八道”?做完 RAG 知识库,我看懂了它的底层逻辑
后端
秋97 小时前
java项目中cpu飙升排查及解决方法
java·开发语言
野生技术架构师7 小时前
牛客网2026最新大厂Java高频面试题精选(附标准答案)
java·开发语言
PH = 77 小时前
JAVA的SPI机制
java·开发语言
一 乐7 小时前
高校实习信息发布网站|基于Spring Boot的高校实习信息发布网站的设计与实现(源码+数据库+文档)
java·数据库·spring boot·后端·论文·毕设·高校实习信息发布网站