【Java 开发日记】我们来说一说什么是 AQS ?

【Java 开发日记】我们来说一说什么是 AQS ?

目录

一、AQS 是什么?

二、AQS 的核心结构

1. 同步状态(State)

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

3. 条件队列(Condition Object)

三、AQS 的设计与关键方法

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

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

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

五、AQS 的应用举例

六、总结


一、AQS 是什么?

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

  • 抽象:它是一个抽象类,本身不能直接实例化,需要子类去继承它,并实现其保护方法来管理同步状态。
  • 队列:它内部维护了一个先进先出(FIFO)的等待队列,用于存放那些没有抢到锁的线程。
  • 同步器:它是构建锁和其他同步组件(如 Semaphore、CountDownLatch 等)的基础框架

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

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

二、AQS 的核心结构

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

1. 同步状态(State)

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

private volatile int state;

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

  • 在 ReentrantLock 中,state 表示锁被同一个线程重复获取的次数。state=0 表示锁空闲,state=1 表示锁被占用,state>1 表示锁被重入。
  • 在 Semaphore 中,state 表示当前可用的许可数量。
  • 在 CountDownLatch 中,state 表示计数器当前的值。

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

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

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

  • 头节点(Head):指向获取到资源的线程所在的节点。头节点不持有线程,是一个“虚节点”。
  • 尾节点(Tail):指向队列中最后一个节点。

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

主要原理图如下:

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

3. 条件队列(Condition Object)

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

  • 当线程调用 Condition.await() 时,会释放锁,并将当前线程构造成节点加入条件队列,然后阻塞。
  • 当线程调用 Condition.signal() 时,会将条件队列中的第一个等待节点转移到 AQS 的等待队列中,等待重新获取锁。

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

三、AQS 的设计与关键方法

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

  1. 独占模式(Exclusive):一次只有一个线程能执行,如 ReentrantLock。
  2. 共享模式(Shared):多个线程可以同时执行,如 Semaphore、CountDownLatch。

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

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

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

独占模式:

  • boolean tryAcquire(int arg):尝试以独占方式获取资源。成功返回 true,失败返回 false。
  • boolean tryRelease(int arg):尝试以独占方式释放资源。成功返回 true,失败返回 false。

共享模式:

  • int tryAcquireShared(int arg):尝试以共享方式获取资源。负数表示失败;0 表示成功,但后续共享获取可能失败;正数表示成功,且后续共享获取可能成功。
  • boolean tryReleaseShared(int arg):尝试以共享方式释放资源。

其他:

  • boolean isHeldExclusively():当前同步器是否在独占模式下被线程占用。在 Condition 相关操作中会用到。
供外部调用的重要方法(Public)

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

独占模式:

  • void acquire(int arg):以独占模式获取资源,忽略中断。如果获取失败,会进入等待队列。
  • void acquireInterruptibly(int arg):同上,但响应中断。
  • boolean tryAcquireNanos(int arg, long nanosTimeout):在 acquireInterruptibly 基础上增加了超时限制。
  • boolean release(int arg):以独占模式释放资源。

共享模式:

  • void acquireShared(int arg):以共享模式获取资源。
  • void acquireSharedInterruptibly(int arg):响应中断的共享获取。
  • boolean tryAcquireSharedNanos(int arg, long nanosTimeout):带超时的共享获取。
  • boolean releaseShared(int arg):以共享模式释放资源。

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

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

public final void acquire(int arg) { if (!tryAcquire(arg) && // 1. 尝试直接获取资源(子类实现) acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 2. 获取失败,则加入队列;3. 在队列中自旋/阻塞等待 selfInterrupt(); // 如果在等待过程中被中断,补上中断标记 }
  1. tryAcquire(arg)
    • 这是子类实现的方法。比如在 ReentrantLock 的非公平锁实现中,它会直接尝试使用 CAS 修改 state,如果成功,就将当前线程设置为独占线程。
    • 如果 tryAcquire 成功,整个 acquire 方法就结束了,线程继续执行。
    • 如果失败,进入下一步。
  2. addWaiter(Node.EXCLUSIVE)
    • 创建一个代表当前线程的 Node 节点,模式为独占模式(Node.EXCLUSIVE)
    • 通过 CAS 操作,快速地将这个新节点设置为尾节点。如果失败,则进入 enq(node) 方法,通过自旋 CAS 的方式确保节点被成功添加到队列尾部。
  3. acquireQueued(final Node node, int arg)
    • 这是核心中的核心。节点入队后,会在这个方法里进行自旋(循环)等待。
    • 在循环中,它会检查自己的前驱节点是不是头节点(p == head)。如果是,说明自己是队列中第一个等待的线程,会再次调用 tryAcquire 尝试获取资源(因为此时锁可能刚好被释放了,这是一个避免不必要的线程挂起、提高性能的优化)。
    • 如果获取成功,就将自己设为新的头节点,然后返回。
    • 如果前驱不是头节点,或者再次尝试获取失败,则会调用 shouldParkAfterFailedAcquire 方法,检查并更新前驱节点的状态(比如将其 waitStatus 设置为 SIGNAL,表示“当你释放锁时,需要唤醒我”)。
    • 如果一切就绪,就调用 parkAndCheckInterrupt() 方法,使用 LockSupport.park(this) 阻塞(挂起)当前线程
    • 当线程被唤醒后(通常是由前驱节点释放锁时 unpark 的),会再次检查自己是否是头节点的后继,并重复上述自旋过程,直到成功获取资源。
  4. selfInterrupt()
    • 如果在等待过程中线程被中断,acquireQueued 方法会返回 true,这里会调用 selfInterrupt 补上中断标志,因为 AQS 在 acquire 过程中是忽略中断的。

释放流程(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 包的基石,几乎所有的同步工具都基于它:

  • ReentrantLock:使用 AQS 的独占模式,state 表示重入次数。
  • ReentrantReadWriteLock:读写锁。AQS 的 state 高16位表示读锁状态,低16位表示写锁状态。
  • Semaphore:使用 AQS 的共享模式,state 表示可用许可数。
  • CountDownLatch:使用 AQS 的共享模式,state 表示计数器值。countDown() 是 releaseSharedawait() 是 acquireShared
  • ThreadPoolExecutor:其内部的工作线程 Worker 类,也继承了 AQS,用于实现独占锁,来判断线程是否空闲。

六、总结

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

它的优点:

  1. 极大地降低了构建锁和同步器的复杂度
  2. 性能高效:通过自旋、CAS 等无锁编程技术,减少了线程上下文切换的开销。
  3. 灵活强大:通过两种模式的区分,可以构建出各种复杂的同步工具。

如果小假的内容对你有帮助,请点赞评论收藏。创作不易,大家的支持就是我坚持下去的动力!

Read more

windows部署的OpenClaw接入飞书机器人

windows部署的OpenClaw接入飞书机器人

文章目录 * 前言 * 一、创建飞书应用 * 1.登录飞书开放平台 * 2.创建企业自建应用 * 3.发布企业自建应用 * 二、OpenClaw配置接入飞书 * 1.安装飞书插件 * 2.配置飞书事件回调 * 3.使用测试 * 总结 前言 OpenClaw 原生支持的即时通信平台主要是海外的 WhatsApp、Telegram、Discord、Slack、iMessage 等,国内用户不习惯,目前国产即时通信软件大厂也跟进了,现在钉钉,飞书等都已支持接入OpenClaw,本文主要是配置飞书机器人接入 OpenClaw,使大家可以通过飞书即可指挥OpenClaw为我们干活,当然配置钉钉接入也可以作为参考。 * windowsWindows 本地(PowerShell)一键部署 OpenClaw * 飞书账号(有飞书开放平台权限的账号) 一、创建飞书应用 1.登录飞书开放平台 1.1 网页访问,

By Ne0inhk

【OpenClaw】揭秘 Secure DM Pairing:如何为你的 AI 机器人构建安全私信访问机制

【OpenClaw】揭秘 Secure DM Pairing:如何为你的 AI 机器人构建安全私信访问机制 在构建基于 LLM 的聊天机器人(如 Telegram、WhatsApp Bot)时,如何控制谁能与机器人对话是一个核心安全问题。直接开放访问可能导致 Token 滥用,而手动配置白名单又过于繁琐。 OpenClaw 提供了一套优雅的解决方案,称为 “Secure DM Pairing” (安全私信配对)。本文将深入解析这套机制的运作流程、使用指令以及底层的代码实现。 注意本文基于 OpenClaw v2026.1.29 版本源码分析。 1. 什么是 Secure DM Pairing? Secure DM Pairing 是 OpenClaw 网关默认的一种访问控制策略。 当一个未授权的用户首次通过私信(Direct Message)

By Ne0inhk
共绩算力 RTX 5090 极速部署 Stable Diffusion WebUI:新手也能秒开 AI 绘图工作站

共绩算力 RTX 5090 极速部署 Stable Diffusion WebUI:新手也能秒开 AI 绘图工作站

还在为本地硬件不足跑不动 AI 绘图模型发愁?想快速拥有高性价比的 Stable Diffusion 绘图环境?今天给大家带来共绩算力 RTX 5090 部署 Stable Diffusion WebUI(增强版)的详细教程,全程零兼容冲突,从云主机配置到生成第一张 AI 画作仅需 30 分钟,步骤清晰可复现,无论是设计爱好者还是 AI 新手都能轻松上手! 目录 一、为什么选择共绩算力部署 Stable Diffusion? 二、环境准备:精准配置云主机 2.1 创建云主机实例 1.2 登录云主机终端 二、完整部署流程 2.1 环境清理与依赖安装 2.2 下载与配置Stable Diffusion WebUI

By Ne0inhk