【JUC】三十、什么是AQS

0、背景

一段常见的代码:

Lock lock = new ReentrantLock();
lock.lock();
try{
   
	//do Something
} finally{
   
	lock.unlock();
}

简单的一个加锁解锁,多线程来抢锁,抢不到锁的线程被分配到哪儿去了?放到一个队列中,后续再出对入队?队列底层又是怎么维护的?

1、AQS介绍

AQS,即AbstractQueuedSynchronizer,抽象的队列同步器。AQS是JUC的基石,好比JVM之于Java。

AQS是用来实现锁或者其它同步器组件的公共基础部分的抽象实现, 是重量级基础框架及整个JUC体系的基石,主要用于解决锁分配给"谁"的问题

AQS相关类是:

在这里插入图片描述

  • AbstractOwnableSynchronizer(下面两兄弟的父类)
  • AbstractQueuedLongSynchronizer:since1.6
  • AbstractQueuedSynchronizer:简称AQS,since1.5

三者都是抽象类。(抽象类,相比接口的全抽象,属半抽象,完成了一部分逻辑,剩余一部分定义了方法规范,给子类去继承和重写)

2、AQS核心概念

以下是AQS类的解释,关键词:先进先出的一个队列 + state状态值

在这里插入图片描述

类比人(线程)去银行(共享资源对象)排队办理业务,有人到窗口开始办时,指示灯变红(state=1,getState、setState),代表有人,其余人去候客区的椅子上等着:

在这里插入图片描述

这个候客区,整体就是一个抽象的FIFO队列来完成对获取资源线程的排队工作,并通过一个int类变量表示持有锁的状态

在这里插入图片描述

这个抽象队列,称为CLH队列,CLH即Craig、Landin and Hagersten,是三个人名,是一个单向链表,但AQS中的队列是CLH变体的虚拟双向队列FIFO,等待的线程就进入这个队列。

3、AQS是JUC的基石

之所以成AQS是JUC的基石,是因为JUC的很多东西底层都是通过AQS实现的,如图:

在这里插入图片描述

举个例子,比如ReentrantLock:

在这里插入图片描述

4、锁和同步器的关系

锁:

  • 面向普通开发者,给开发者用的
  • 定义了开发者和锁交互使用的API,比如lock、unlock,隐藏了实现细节,调用即可

同步器:

  • 面向锁的实现者,Java并发的缔造者
  • 提出统一规范并简化了锁的实现,屏蔽了同步状态管理、同步队列的管理和维护、阻塞线程排队和通知、唤醒机制等公共的底层细节,将其抽象出来,形成抽象的公共基类,做为一切锁和同步组件实现的公共基础部分

5、AQS的作用

加锁会导致阻塞,有阻塞,抢不到的线程就需要排队,实现排队必然需要队列。类比银行办理业务的窗口满了,其他人去候客区等待,但等待的线程仍然保留了获取锁的可能,且获取锁的流程仍在继续(候客区的顾客也在等着被叫到号)。

在这里插入图片描述

如果共享资源被占用,就需要一定的阻塞、唤醒机制来保证锁分配。这个机制主要用的是CLH队列的变体实现的,将暂时获取不到锁的线程加入到队列中,这个队列就是AQS同步队列的抽象表现。它将要请求共享资源的线程及自身的等待状态封装成队列的结点对象 Node(候客区的一把把椅子),通过CAS自旋以及LockSupport.park()的方式,维护state变量的状态,使并发达到同步的效果。

在这里插入图片描述

AQS使用一个volatile的int类型的成员变量state来表示同步状态,通过内置的FIFO队列来完成资源获取的排队工作,将每个要去抢占资源的线程封装成一个Node节点来实现锁的分配,通过CAS完成对State值的修改。

在这里插入图片描述

6、state和CLH队列

AQS类、Node类以及JUC相关类的关联关系如下:

在这里插入图片描述

AQS类,有个int类型的成员变量state:

private volatile int state;

它就类似去银行办理业务的受理窗口状态。0就是没人,自由状态可办理,大于等于1就是有人占用窗口,其他人坐在候客区(队列)的椅子上(Node)等着去。而CLH队列,一个双向队列,就是候客区,队列中装的就是一个个Node内部类的对象。

在这里插入图片描述

总结:AQS的实质就是CLH的双端队列,加一个state变量,队列中排队的每个个体就是一个Node。

7、AQS的内部类Node

Node的成员变量含义:

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

        /** 线程被取消了,即排队在Node里的线程不想排了 */
        static final int CANCELLED =  1;
        /** 后继线程需要被唤醒*/
        static final int SIGNAL    = -1;
        /** 等待condition唤醒 */
        static final int CONDITION = -2;
        /**共享式同步状态获取将会无条件地传播下去*/
        static final int PROPAGATE = -3;

        /**初始为0,状态是上面的1,-1,-2,-3这几种
        * 代表Node里面线程的等待状态,其取值不同,后面的出队入队也就跟着受影响,和AQS的state是两个东西
        */
        volatile int waitStatus;

        /**当前node的前置节点*/
        volatile Node prev;

        /**当前node的后置节点
         */
        volatile Node next;

        /**坐node这个椅子里的线程*/
        volatile Thread thread;

       /** 构造方法、实例方法、静态代码块略*/

}

在这里插入图片描述

相关推荐

  1. 什么ac

    2023-12-20 05:40:04       19 阅读
  2. AI什么

    2023-12-20 05:40:04       15 阅读
  3. 什么ar.exe?

    2023-12-20 05:40:04       11 阅读

最近更新

  1. TCP协议是安全的吗?

    2023-12-20 05:40:04       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2023-12-20 05:40:04       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2023-12-20 05:40:04       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2023-12-20 05:40:04       20 阅读

热门阅读

  1. Unity工具栏介绍

    2023-12-20 05:40:04       35 阅读
  2. 深度比较(lodash 的 isEqual 方法)

    2023-12-20 05:40:04       40 阅读
  3. 通过fu过 Function Calling 查询数据库

    2023-12-20 05:40:04       34 阅读
  4. 普冉(PUYA)单片机开发笔记 [完结篇]:使用体会

    2023-12-20 05:40:04       48 阅读
  5. 高空作业MR混合现实情景实训教学应用

    2023-12-20 05:40:04       41 阅读
  6. 【算法小题 go程序版】递归练习 -- 猴子吃桃问题

    2023-12-20 05:40:04       40 阅读
  7. 链式表的实现

    2023-12-20 05:40:04       32 阅读
  8. Docker使用7-Use Docker Compose

    2023-12-20 05:40:04       56 阅读
  9. 【嵌入式面试】嵌入式经典面试题汇总(C语言)

    2023-12-20 05:40:04       34 阅读
  10. Python基础dict字典定义与函数

    2023-12-20 05:40:04       38 阅读