当前位置: 首页 > news >正文

AQS模板方法

一、AQS 是什么?

AbstractQueuedSynchronizer(简称 AQS)是 Java 并发包 java.util.concurrent.locks 中的一个抽象类,它是构建锁和同步器(如 ReentrantLockSemaphoreCountDownLatch 等)的核心框架。

你可以把 AQS 看作一个**“同步状态管理器”**。它解决了并发编程中最核心的问题:

  1. 如何管理一个共享的状态?(比如锁的“被持有”或“未被持有”状态,信号量的“可用许可数”)。
  2. 当多个线程竞争这个状态时,如何让竞争失败的线程排队等待?
  3. 当状态被释放时,如何从等待队列中唤醒一个或多个线程?

AQS 将这些通用的、复杂的线程排队、状态管理、唤醒机制都封装好了,而把“状态是什么”以及“在什么条件下可以获取/释放状态”这个问题留给了它的子类去定义。


二、模板方法模式

在深入 AQS 之前,我们先快速回顾一下模板方法模式

1. 定义

模板方法模式在一个方法中定义了一个算法的骨架,而将一些步骤的实现延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

2. 两个关键角色

  • 模板方法:在父类中定义,它是一个具体方法,包含了算法的骨架。它调用一系列基本方法(可以是抽象的,也可以是具体的)来完成整个流程。
  • 基本方法
    • 抽象方法:由父类声明,子类必须实现。这是算法中可变的部分。
    • 具体方法:由父类实现,子类可以继承或覆盖。这是算法中不变的部分。

3. 简单例子

// 父类:定义制作饮料的流程
abstract class BeverageTemplate {// 这是模板方法,定义了制作饮料的算法骨架(final防止子类覆盖)public final void prepareBeverage() {boilWater(); // 固定步骤brew();      // 可变步骤,由子类实现pourInCup(); // 固定步骤addCondiments(); // 可变步骤,由子类实现}private void boilWater() {System.out.println("Boiling water");}private void pourInCup() {System.out.println("Pouring into cup");}// 抽象方法,由子类实现protected abstract void brew();protected abstract void addCondiments();
}// 子类1:制作咖啡
class Coffee extends BeverageTemplate {@Overrideprotected void brew() {System.out.println("Dripping Coffee through filter");}@Overrideprotected void addCondiments() {System.out.println("Adding Sugar and Milk");}
}// 子类2:制作茶
class Tea extends BeverageTemplate {@Overrideprotected void brew() {System.out.println("Steeping the tea");}@Overrideprotected void addCondiments() {System.out.println("Adding Lemon");}
}

在这个例子中,prepareBeverage() 就是模板方法,它定义了制作饮料的通用流程。而 brew() 和 addCondiments() 就是抽象的基本方法,由子类(CoffeeTea)来提供具体实现。


三、AQS 如何运用模板方法模式

AQS 是模板方法模式在 Java 并发领域最经典、最完美的应用。

1. AQS 中的“模板方法”

AQS 内部提供了一系列对使用者开放的、公有的、final 的方法,这些方法就是模板方法。它们定义了获取和释放同步状态的完整流程。

例如:

  • acquire(int arg):独占模式下获取同步状态。如果获取失败,线程会进入等待队列。
  • release(int arg):独占模式下释放同步状态。如果释放成功,会唤醒等待队列中的第一个线程。
  • acquireShared(int arg):共享模式下获取同步状态。
  • releaseShared(int arg):共享模式下释放同步状态。
  • acquireInterruptibly(int arg):可中断的获取。

这些方法都是 final 的,意味着任何继承 AQS 的子类都不能去修改它们的核心流程。这些流程(比如排队、阻塞、唤醒)已经被 AQS 精心设计并固化了。

2. AQS 中的“基本方法”(钩子方法)

AQS 将“判断是否可以获取/释放状态”这个核心逻辑,抽象成了一系列受保护的、抽象的或可覆盖的方法,供子类实现。这些就是基本方法钩子方法

核心的钩子方法主要有以下几个:

方法名描述模式
tryAcquire(int arg)尝试以独占模式获取状态。如果成功,返回 true。这是实现 ReentrantLock 等独占锁的核心。独占
tryRelease(int arg)尝试以独占模式释放状态。如果释放后允许唤醒后继节点,返回 true独占
tryAcquireShared(int arg)尝试以共享模式获取状态。返回值大于等于0表示成功,负数表示失败。这是实现 SemaphoreCountDownLatch 的核心。共享
tryReleaseShared(int arg)尝试以共享模式释放状态。如果释放后可能需要唤醒后继节点,返回 true共享
isHeldExclusively()查询当前同步器是否被当前线程独占。Condition 的实现需要它。独占

关键点:这些方法在 AQS 中默认会抛出 UnsupportedOperationExceptionAQS 强制子类必须根据自己想要的同步语义(是独占锁还是共享锁)去实现它们

3. AQS 的“状态”——state 变量

AQS 内部维护了一个 private volatile int state 变量。这个 state 就是同步状态,它的具体含义完全由子类来定义。

  • 对于 ReentrantLockstate 可以表示锁被重入的次数。0 表示未被持有,1 表示被持有一次,N 表示被重入 N 次。
  • 对于 Semaphorestate 表示剩余的可用许可数。
  • 对于 CountDownLatchstate 表示需要倒计数的初始值。

AQS 提供了 getState()setState(int newState)compareAndSetState(int expect, int update) 这三个 final 方法来安全地操作 state,子类通过这些方法来管理状态。


四、实例解析:ReentrantLock 如何基于 AQS 实现

ReentrantLock 内部有一个 Sync 类,它继承自 AQS

1. 定义状态

ReentrantLock 将 AQS 的 state 定义为锁的持有计数。

2. 实现钩子方法

ReentrantLock 有公平锁和非公平锁两种模式,它们分别对应 Sync 的两个子类 FairSync 和 NonfairSync。我们以非公平锁为例,看看它是如何实现 tryAcquire 的。

// ReentrantLock.NonfairSync 中的简化版 tryAcquire
final boolean nonfairTryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState(); // 获取当前状态(持有次数)if (c == 0) { // 如果状态为0,表示锁未被持有// 使用 CAS 尝试将状态从 0 设置为 acquires(通常是1)if (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current); // 如果成功,设置当前线程为独占者return true; // 获取成功}}// 如果状态不为0,检查持有锁的线程是不是自己else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires; // 如果是,增加重入次数if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");setState(nextc); // 直接设置状态,因为已经是独占状态,不需要CASreturn true; // 重入成功}// 否则,获取失败return false;
}

分析:

  • 这个 nonfairTryAcquire 方法就是 AQS 的 tryAcquire 钩子方法的具体实现。
  • 它定义了“非公平地获取一个可重入锁”的业务逻辑:先抢一下(CAS),抢不到再看是不是自己的锁(重入)。
  • 它只关心“能不能拿到锁”,至于拿不到怎么办(排队、阻塞),它完全不关心。
3. 调用流程

当你的代码调用 lock.lock() 时:

  1. lock() 方法会调用 AQS 的模板方法 acquire(1)
  2. acquire(1)(模板方法)的内部逻辑大概是:
    public final void acquire(int arg) {if (!tryAcquire(arg) && // 调用子类实现的 tryAcquireacquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 如果失败,则入队并阻塞selfInterrupt();}
  1. AQS 的 acquire 方法首先调用 tryAcquire(1),也就是我们上面看到的 nonfairTryAcquire(1)
  2. 如果 tryAcquire 返回 trueacquire 方法执行完毕,线程成功获取锁,继续执行。
  3. 如果 tryAcquire 返回 false,AQS 就会接管后续工作:将当前线程包装成一个节点(Node)加入到内部的 CLH 等待队列中,然后通过 LockSupport.park() 将线程挂起(阻塞)。

这就是模板方法模式的精髓:AQS 定义了“获取锁”这个复杂流程的骨架(acquire),而将“如何判断锁是否可用”这个核心逻辑(tryAcquire)留给子类(ReentrantLock)去实现。


五、总结

特性描述
AQS 角色模板方法模式中的抽象父类。它定义了同步器的通用算法骨架。
模板方法acquire()release()acquireShared() 等。它们是 final 的,定义了获取/释放状态的完整流程,包括排队、阻塞、唤醒等不变逻辑。
基本方法(钩子)tryAcquire()tryRelease()tryAcquireShared() 等。它们是 protected 的,由子类实现,定义了“状态是否可获取/释放”的可变逻辑。
核心状态volatile int state。其具体含义由子类定义,AQS 只提供安全的访问方法(getStatesetStateCAS)。
设计优势解耦复用。AQS 将复杂的并发控制逻辑与具体的同步语义分离开。开发者只需关注自己的业务逻辑(如何定义状态和获取/释放条件),而无需关心底层的线程排队和调度,极大地简化了高性能同步器的开发难度。

通过这种设计,Java 并发包能够以 AQS 为基础,高效地构建出 ReentrantLockSemaphoreCountDownLatchReentrantReadWriteLock 等功能各异但底层机制统一的同步工具,是 Java 并发库设计的基石。

http://www.dtcms.com/a/346359.html

相关文章:

  • 使用 Google 开源 AI 工具 LangExtract 进行结构化信息抽取
  • 单片机---------WIFI模块
  • Seaborn数据可视化实战:Seaborn数据可视化入门-绘制统计图表与数据分析
  • Dify 从入门到精通(第 49/100 篇):Dify 的自动化测试
  • STM32 硬件I2C读写MPU6050
  • 【链表 - LeetCode】24. 两两交换链表中的节点
  • 纯手撸一个RAG
  • 黄飞对话小熊电器流程与IT负责人:企业数字化进阶与AI实践如何落地?
  • QIcon::actualSize的作用和用法
  • 2025/8/22 xxl-job速通
  • 解决 微信开发者工具 :下载基础库版本 2.31.0 失败
  • RAG和微调是什么?两者的区别?什么场景使用RAG或微调?判断依据是什么?
  • LINUX网络编程--网络的发展与通信
  • AI赋能环保精准治理:AI水质监测溯源快、空气质量预测施策准,守护生态新效能
  • 关于 java+gradle的弹窗多选应用app
  • 【GPT入门】第54课 量化位数与存储大小的影响
  • Java 面试题训练助手 Web 版本
  • 网络通信——UDP协议。
  • Kubernetes 1.28 集群部署指南(基于 Containerd 容器运行时)
  • 笔记:二叉树构建方法
  • 从“配置化思维”到“前端效率革命”:xiangjsoncraft 如何用 JSON 简化页面开发?
  • 【源码】MES系统:从下达计划、执行反馈、异常预警到过程控制的一整套执行中枢。
  • FastTracker:实时准确的视觉跟踪
  • 一键部署openGauss6.0.2轻量版单节点
  • DPY-3010: connections to this database server version are not supported by p
  • LoRA内幕机制解析(53)
  • Design Compiler:层次模型(Block Abstraction)的简介
  • 什么是神鸟云?
  • 亚马逊老品怎么再次爆发流量?
  • 软件测试要怎么自学?