【Java基础】AQS (AbstractQueuedSynchronizer) 抽象队列同步器

这篇具有很好参考价值的文章主要介绍了【Java基础】AQS (AbstractQueuedSynchronizer) 抽象队列同步器。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。
专注于分享各领域原创系列文章 ,擅长java后端、移动开发、人工智能等,希望大家多多支持。

一、导读

我们继续总结学习Java基础知识,温故知新。

1.1 CLH锁

CLH(Craig, Landin, and Hagersten locks)是一种自旋锁,能确保无饥饿性,提供先来先服务的公平性。
CLH锁是一种基于链表的可扩展、高性能、公平的自旋锁,申请线程只在本地变量上自旋,它不断轮询前驱的状态,如果发现前驱释放了锁就结束自旋。
【Java基础】AQS (AbstractQueuedSynchronizer) 抽象队列同步器,java,开发语言

二、概览

AbstractQueuedSynchronizer 是抽象队列同步器,是一种用来构建锁和同步器的框架

AQS主要做了三件事情

  • 同步状态的管理
  • 线程的阻塞和唤醒
  • 同步队列的维护

AQS 定义了同步器的基本操作,如获取、释放和状态管理,并提供了一个等待队列来管理等待资源的线程,解决了在实现同步器时涉及的大量细节问题,例如自定义标准同步状态、FIFO 同步队列。

基于 AQS 来构建同步器可以带来很多好处。它不仅能够极大地减少实现工作,而且也不必处理在多个位置上发生的竞争问题。

三、使用场景

AQS 是一个相对底层的同步器框架,对于一些常见的同步需求,Java 并发库已经提供了许多高级封装,如 ReentrantLock、ReadWriteLock、Semaphore 等,这些高级封装已经为我们提供了更简单易用的接口和功能。因此,在应用开发中,直接使用 AQS 的场景相对较少,更多的是通过使用它的子类来实现具体的同步机制。

常用的同步器有:

  1. 独占锁(如 ReentrantLock):AQS 提供了 acquire(int arg) 和 release(int arg) 等方法,开发人员可以继承 AQS 并实现自定义的同步器来实现独占锁。通过控制同步状态(通过 getState() 和 setState(int newState) 方法),以及管理等待线程(通过等待队列),AQS 可以提供可重入锁、公平锁等不同类型的独占锁。

  2. 共享锁(如 ReadWriteLock):AQS 也可以用于实现共享锁机制,例如 ReentrantReadWriteLock。通过 acquireShared(int arg) 和 releaseShared(int arg) 等方法,开发人员可以自定义实现共享锁的逻辑。AQS 提供了对多个读线程和写线程的管理和协调,以及对读线程的优化。

  3. 实现其他同步工具:AQS 的框架还可以用于实现其他类似的同步工具,如信号量(Semaphore)、倒计时器(CountDownLatch)、循环屏障(CyclicBarrier)等。

通过继承 AQS 并自定义同步器的行为,可以实现不同的同步机制。

3.1 AQS 对资源的共享方式

  1. Exclusive(独占):只有一个线程能执行,如ReentrantLock。

    资源锁可分为公平锁和非公平锁:

  • 公平锁:按照线程在队列中的排队顺序(FIFO),先到者先拿到锁。
    【Java基础】AQS (AbstractQueuedSynchronizer) 抽象队列同步器,java,开发语言

  • 非公平锁:当线程要获取锁时,无视队列顺序直接去抢锁,谁抢到就是谁的(被唤醒的线程和新来的线程重新竞争锁)。
    【Java基础】AQS (AbstractQueuedSynchronizer) 抽象队列同步器,java,开发语言

  1. Share(共享):多个线程可同时执行,如Semaphore/CountDownLatch。Semaphore、CountDownLatCh、 CyclicBarrier、ReadWriteLock 我们都会在后面讲到。

四、原理

AQS大致流程如下:
1、当某一线程获取锁后,将state值+1,并记录下当前持有锁的线程。
2、再有线程来获取锁时,判断这个线程与持有锁的线程是否是同一个线程,如果是,将state值再+1,如果不是,阻塞线程(调用 LockSupport.park(this)挂起线程)。
3、当线程释放锁时,将state值-1。
4、当state值减为0时,表示当前线程彻底释放了锁。
5、然后将记录当前持有锁的线程的那个字段设置为null,并唤醒其他线程,使其重新竞争锁

4.1 原理

AQS使用一个 Volatile的 int类型的成员 state 变量来表示同步状态,通过内置的FIFO队列来完成资源获取的排队工作(双向链表,多线程争用资源被阻塞时会进入此队列),然后通过CAS完成对State值的修改。

其并发控制的核心是锁的获取与释放,锁的实现方式有很多种,AQS采用的是一种改进的CLH锁。


    当state=0表示释放了锁,当state>0表示获得锁
    /**
     * The synchronization state.
     */
    private volatile int state;
    
    
	封装一个Node,包含前节点,后节点,组成一个双向队列。
	private transient volatile Node head;

	private transient volatile Node tail;

【Java基础】AQS (AbstractQueuedSynchronizer) 抽象队列同步器,java,开发语言

CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系)。

AQS是将每条请求共享资源的线程封装成一个CLH锁队列(FIFO同步队列)的一个结点(Node)来实现锁的分配。

如果线程获取当前同步状态失败,AQS会将当前线程的信息封装成一个Node节点,加入同步队列中,并且阻塞该线程,当同步状态释放,则会将队列中的线程唤醒,重新尝试获取同步状态。

我们看下node源码:

static final class Node {
    /** 共享节点 */
    static final Node SHARED = new Node();
    /** 独占节点 */
    static final Node EXCLUSIVE = null;

    当前节点在队列中的状态
    volatile int waitStatus;

    前驱指针
    volatile Node prev;

    后继指针
    volatile Node next;

    表示处于该节点的线程
    volatile Thread thread;

    指向下一个处于CONDITION状态的节点
    Node nextWaiter;
}

五、 推荐阅读

【Java基础】原子性、可见性、有序性

【Java基础】java可见性之 Happens-before

【Java基础】java-android面试Synchronized

【Java基础】java-android面试-线程状态

【Java基础】线程相关

【Java基础】java 异常

【Java基础】java 反射

【Java基础】java 泛型

【Java基础】java注解

【Java基础】java动态代理

【Java基础】Java SPI

【Java基础】Java SPI 二 之 Java APT

【Java基础】 jvm 堆、栈、方法区 & java 内存模型

【Java基础】volatile关键字

【Java基础】线程同步类 CountDownLatch

【Java基础】CAS (Compare And Swap) 操作文章来源地址https://www.toymoban.com/news/detail-549044.html

到了这里,关于【Java基础】AQS (AbstractQueuedSynchronizer) 抽象队列同步器的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 万字长文解析AQS抽象同步器核心原理(深入阅读AQS源码)

    在争用激烈的场景下使用基于CAS自旋实现的轻量级锁有两个大的问题: CAS恶性空自旋会浪费大量的CPU资源。 在SMP架构的CPU上会导致“总线风暴”。 解决CAS恶性空自旋的有效方式之一是以空间换时间,较为常见的方案有两种:分散操作热点、使用队列削峰。 JUC并发包使用的是

    2024年02月11日
    浏览(44)
  • AQS源码解读----AbstractQueuedSynchronizer

     

    2024年02月06日
    浏览(31)
  • 并发专栏-队列同步器 AQS 以及 Reentrantlock 应用

    Java 中的大部分同步类都是基于AbstractQueuedSynchronizer(简称为AQS)实现的。 ReentrantLock 、 ReentrantReadWriteLock 、 Semaphore(信号量) 、 CountDownLatch 、 公平锁 、 非公平锁 、 ThreadPoolExecutor 都和 AQS 有直接关系,所以了解 AQS 的抽象实现,并在此基础上结合上述各类的实现细节,很快就

    2024年02月07日
    浏览(45)
  • 10分钟从源码级别搞懂AQS(AbstractQueuedSynchronizer)

    上篇文章15000字、6个代码案例、5个原理图让你彻底搞懂Synchronized有说到synchronized由object monitor实现的 object monitor中由cxq栈和entry list来实现阻塞队列,wait set实现等待队列,从而实现synchronized的等待/通知模式 而JDK中的JUC并发包也通过类似的阻塞队列和等待队列实现等待/通知模

    2024年02月10日
    浏览(44)
  • 学Java线程,你不知道什么是AQS?一文带你了解Java多线程同步的灵魂

    关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 ,擅长java后端、移动开发、人工智能等,希望大家多多支持。 我们继续总结学习 Java基础知识 ,温故知新。 CLH(Craig, Landin, and Hagersten locks)是一种自旋锁,能确保无饥饿性,提

    2024年02月16日
    浏览(47)
  • Java学数据结构(1)——抽象数据类型ADT & 表List、栈Stack和队列Qeue

    1.抽象数据类型Abstract data type的概念; 2.表list,java中的ArrayList和linkedlist以及vector的分析; 3.栈stack的分析以及应用; 4.队列queue的理解,以及rabbitmq的应用; 抽象数据类型(abstract data type,ADT)是带有一组操作的一些对象的集合。抽象数据类型是数学的抽象;在ADT的定义中没有地

    2024年02月11日
    浏览(42)
  • AQS构建锁和同步器的框架

    AQS全称AbstractQueuedSynchronizer,此类在 java.util.concurrent.locks包下面 ,是一个 构建锁和同步器的框架 ,比如 ReentrantLock就是基于AQS来实现的 。 AQS内部有一个由 volatile修饰(保证其可见性) 的变量 state ,用此来表示锁是否被使用,初始化为0,当线程获取到锁时,state加1,此时当其

    2024年02月14日
    浏览(35)
  • Java抽象类(基础详解)

    目录 1.概念 2.抽象类的使用 2.1抽象方法 2.2抽象类 2.3抽象类的使用 2.4注意事项 普通类是一个完善的功能类,可以直接产生实例化对象,并且在普通类中可以包含有构造方法,普通方法,static方法,常量和变量等内容。而抽象类是指在普通类的结构里面增加抽象方法的组成部

    2024年02月06日
    浏览(36)
  • 【JavaSE】Java基础语法(十六):抽象类

    当我们在做子类共性功能抽取时,有些方法在父类中并没有具体的体现,这个时候就需要抽象类了! 在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽 象类! 抽象类和抽象方法必须使用 abstract 修饰 抽象类中不一定有抽

    2024年02月07日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包