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

Java 并发编程 —— AQS 抽象队列同步器

文章目录

      • 什么是 AQS
      • 底层数据结构—— CLH 队列
      • 入队和出队
      • 状态标志位
      • AQS 的代码设计思路
      • AQS 提供的钩子方法
      • 参考资料

什么是 AQS

AQS 是 JUC 提供的一个用于构建锁和同步容器的基础类,用于减少由于无效争夺导致的资源浪费和性能恶化。JUC 包内的许多类都是基于 AQS 构建, 例如 ReentrantLock、Semaphore、CountDownLatch、ReentrantReadWriteLock、FutureTask 等。AQS 解决了在实现同步容器时设计的大量细节问题。

public abstract class AbstractQueuedSynchronizerextends AbstractOwnableSynchronizerimplements java.io.Serializable {
}

AQS 的核心思想是, 如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制。

这个机制是基于 CLH 锁 (Craig, Landin, and Hagersten locks) 实现的, 即一个虚拟的双向队列。AQS 将每条请求共享资源的线程封装成一个 CLH 队列锁的一个结点(Node)来实现锁的分配。

底层数据结构—— CLH 队列

AQS(AbstractQueuedSynchronizer)内部维护的确是一个 FIFO(先进先出) 的双向链表结构,用于管理线程的同步状态。这个双向链表的结构特点是,能够从任意节点很方便地访问它的前驱和后继节点,从而在需要唤醒某个线程时,可以快速地找到并操作相关节点。

AQS 的 Node 节点是封装了线程的基本单元,它们存储线程的信息以及线程的状态。每个线程在争抢锁失败后,会被封装成一个 Node 节点,并添加到队列的尾部。当持有锁的线程释放锁时,它会唤醒队列中第一个等待的节点(FIFO),该节点的线程将重新尝试获取锁。如果成功获取锁,该线程将继续执行。

  private transient volatile Node head;private transient volatile Node tail;abstract static class Node {volatile Node prev;       // initially attached via casTailvolatile Node next;       // visibly nonnull when signallableThread waiter;            // visibly nonnull when enqueuedvolatile int status;      // written by owner, atomic bit ops by others...}

在这里插入图片描述

入队和出队

每当线程通过 AQS 获取锁失败时,线程将被封装成一个 Node 节点,通过 CAS 原子操作插入队列尾部。当有线程释放锁时,AQS 会尝试让队首的后驱节点占用锁。AQS的队首节点和队尾节点都是懒加载的。

    final void enqueue(Node node) {if (node != null) {for (;;) {Node t = tail;node.setPrevRelaxed(t);        // avoid unnecessary fenceif (t == null)                 // initializetryInitializeHead();else if (casTail(t, node)) {t.next = node;if (t.status < 0)          // wake up to clean linkLockSupport.unpark(node.waiter);break;}}}}

在这里插入图片描述

状态标志位

state 用于表示同步状态,volatile 确保多线程环境下,对 state 的修改能立即反映到其他线程中,常用于简单的状态标记或轻量级的同步控制。在 ReentrantLock 类中,state 就用于实现可重入锁。

同时, AQS 提供了 CAS 算法实现的修改方法 compareAndSetState()

public abstract class AbstractQueuedSynchronizerextends AbstractOwnableSynchronizerimplements java.io.Serializable {/*** The synchronization state.*/private volatile int state;protected final int getState() {return state;}protected final void setState(int newState) {state = newState;}// CAS 修改方法protected final boolean compareAndSetState(int expect, int update) {return U.compareAndSetInt(this, STATE, expect, update);}}

AQS 的代码设计思路

AQS 出于“分离变与不变”的原则,基于模板模式实现AQS 为锁获取、锁释放的排队和出队过程提供了一系列的模板方法。由于 JUC 的显式锁种类丰富,因此 AQS 将不同锁的具体操作抽取为钩子方法,供各种锁的子类(或者其内部类)去实现。

显示锁和 AQS之间的关系为组合关系。

在这里插入图片描述

AQS 提供的钩子方法

AQS 针对共享锁和独享锁这两种资源共享方式提供了不同的模板方法,定义了不同的钩子方法:

  • tryAcquire(int):独占锁钩子,尝试获取资源。若成功则返回true,若失败则返回false。
  • tryRelease(int):独占锁钩子,尝试释放资源。若成功则返回true,若失败则返回false。
  • tryAcquireShared(int):共享锁钩子,尝试获取资源,负数表示失败;0表示成功,但没有剩 余可用资源;正数表示成功,且有剩余资源。
  • tryReleaseShared(int):共享锁钩子,尝试释放资源。若成功则返回true,若失败则返回false。
  • isHeldExclusively():独占锁钩子,判断该线程是否正在独占资源。只有用到condition条件 队列时才需要去实现它。

除了钩子方法以外, AQS 以外的其他方法基本都是 final

参考资料

《 极致经典(卷2):Java高并发核心编程(卷2 加强版) -特供v21-release》

AQS 详解 | JavaGuide


http://www.mrgr.cn/news/31225.html

相关文章:

  • 信奥初赛解析:1-3-计算机软件系统
  • ARM/Linux嵌入式面经(三七):CVTE
  • LTE和NR空口技术区别
  • Redis基本命令详解
  • 二、pandas入门
  • arkts基础知识
  • 实例代码逐行解读
  • Git常用指令
  • 想要监控电脑?这十款电脑监控软件推荐!
  • Gitlab学习(007 gitlab项目操作)
  • python使用argparse解析命令行,如何正确传入科学计数法形式的浮点数
  • Unsupervised Domain Adaptation by Backpropagation
  • 十三 系统架构设计(考点篇)试题
  • 工程师 - .NET程序反编译器
  • SQLiteHelper
  • 视频单目标跟踪研究
  • 踩坑记录:adb修改settings数据库ContentObserver无回调
  • 基于存内计算架构的模型部署与映射优化
  • 【洛谷】P1313 [NOIP2011 提高组] 计算系数 的题解
  • MySQL —— 索引