java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > java aqs是什么

Java中的AQS入门攻略

作者:程序员小假

AQS(AbstractQueuedSynchronizer)是JUC包中用于构建锁和同步器的核心框架,它通过volatile的state变量和FIFO队列实现线程同步,简化了同步工具的开发,并支持独占与共享两种模式,本文介绍Java中的AQS是什么,感兴趣的朋友跟随小编一起看看吧

一、AQS 是什么?

AQS,全称 AbstractQueuedSynchronizer,即抽象队列同步器

核心思想:
AQS 使用一个整型的 volatile 变量(state 来表示同步状态(例如,锁被重入的次数、许可的数量等),并通过一个内置的 FIFO 队列来完成资源获取线程的排队工作。

设计模式:
AQS 是 模板方法模式 的经典应用。父类(AQS)定义了骨架和核心算法,而将一些关键的操作以 protected 方法的形式留给子类去实现。这样,实现一个自定义同步器只需要关注如何管理 state 状态即可,至于线程的排队、等待、唤醒等复杂操作,AQS 已经帮我们完成了。

二、AQS 的核心结构

AQS 的核心可以概括为三部分:同步状态(state)等待队列 和 条件队列

1. 同步状态(State)

这是一个 volatile int 类型的变量,是 AQS 的灵魂。

private volatile int state;

它的具体含义由子类决定,非常灵活:

对 state 的操作是原子的,通过 getState()setState(int newState)compareAndSetState(int expect, int update) 等方法进行。

2. 等待队列(CLH 队列的变体)

这是一个双向链表,是 AQS 实现阻塞锁的关键。当线程请求共享资源失败时,AQS 会将当前线程以及等待状态等信息构造成一个节点(Node) 并将其加入队列的尾部,同时阻塞该线程。

当一个线程释放资源时,它会唤醒后继节点,后继节点成功获取资源后,会将自己设置为新的头节点。

主要原理图如下:

AQS 使用一个 Volatile 的 int 类型的成员变量来表示同步状态,通过内置的 FIFO 队列来完成资源获取的排队工作,通过 CAS 完成对 State 值的修改。

3. 条件队列(Condition Object)

AQS 内部类 ConditionObject 实现了 Condition 接口,用于支持 await/signal 模式的线程间协作。每个 ConditionObject 对象都维护了一个自己的单向链表(条件队列)

注意:一个 AQS 实例可以对应多个 Condition 对象(即多个条件队列),但只有一个等待队列。

三、AQS 的设计与关键方法

AQS 将资源获取的方式分为两种:

AQS 提供了顶层的入队和出队逻辑,而将尝试获取资源尝试释放资源的具体策略留给了子类。

需要子类重写的关键方法(Protected)

这些方法在 AQS 中是 protected 的,默认抛出 UnsupportedOperationException

独占模式:

共享模式:

其他:

供外部调用的重要方法(Public)

这些是模板方法,子类一般不重写,使用者(或子类)直接调用。

独占模式:

共享模式:

四、源码级工作流程解析(以acquire为例)

我们来看一下最核心的 acquire 方法,它展示了 AQS 的完整工作流程:

public final void acquire(int arg) {
    if (!tryAcquire(arg) && // 1. 尝试直接获取资源(子类实现)
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 2. 获取失败,则加入队列;3. 在队列中自旋/阻塞等待
        selfInterrupt(); // 如果在等待过程中被中断,补上中断标记
}

释放流程(release)相对简单:

public final boolean release(int arg) {
    if (tryRelease(arg)) { // 1. 子类尝试释放资源
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h); // 2. 唤醒后继节点
        return true;
    }
    return false;
}

unparkSuccessor 会找到队列中第一个需要唤醒的线程(通常是头节点的下一个有效节点),然后调用 LockSupport.unpark(s.thread) 将其唤醒。

五、AQS 的应用举例

AQS 是 JUC 包的基石,几乎所有的同步工具都基于它:

六、总结

AQS 的核心贡献在于,它提供了一个强大的框架,将复杂的线程排队、阻塞、唤醒等底层操作封装起来,让同步器的开发者只需要关注一个核心问题:如何管理那个 state 变量。

它的优点:

到此这篇关于Java中的AQS入门攻略的文章就介绍到这了,更多相关java aqs是什么内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文