JUC高级十-AbstractQueuedSynchronizer之AQS

news2024/9/24 17:20:13

1. 前置知识

  • 公平锁和非公平锁
  • 可重入锁
  • 自旋锁
  • LockSupport
  • 数据结构之双向链表
  • 设计模式之模板设计模式

AQS重要性

JAVA ------>JVM

AQS ------>AQS

2. AQS入门级别理论知识

2.1 是什么?

2.1.1 字面意思

Abstract Queued Synchronizer----抽象的队列同步器

源码位置:

AbstractQueuedSynchronizer和AbstractQueuedLongSynchronizer是AbstractOwnableSynchronizer的子类

image-20230415100107662

并且是一个抽象类:

image-20230415100320554

2.1.2 技术解释

image-20230415102708930

  • 是用来构建锁或者其它同步器组件的重量级基础框架及整个JUC体系的基石,主要用于解决锁分配给"谁"的问题
  • 通过内置的FIFO队列来完成资源获取线程的排队工作,并通过一个int类变量
    表示持有锁的状态

CLH队列(FIFO)

CLH:Craig、Landin and Hagersten 队列,是一个单向链表,AQS中的队列是CLH变体的虚拟双向队列FIFO

image-20230415100847071

2.2 AQS为什么是JUC内容中最重要的基石?

2.2.1 和AQS关联的技术

AQS

JUC的以下技术都是以AQS为基石

  • Semaphore
  • CycleBarrier
  • ReentranReadWriteLock
  • CountDownLatch
  • ReentranLock

AQS子类:

image-20230415101859403

2.2.2 锁和同步器的关系

  • 锁,面向锁的使用者
    • 定义了程序员和锁交互的使用层API,隐藏了实现细节,你调用即可。
  • 同步器,面向锁的实现者
    • 比如Java并发大神DougLee,提出统一规范并简化了锁的实现,
      屏蔽了同步状态管理、阻塞线程排队和通知、唤醒机制等。

2.3 AQS能干嘛?

因为:加锁会导致阻塞

所以:有阻塞就需要排队,实现排队必然需要队列

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

3 AQS内部体系架构

image-20230415103008431

image-20230415102854436

3.1 AQS自身

3.1.1 AQS的int变量state-volatile修饰

代表同步状态标志位

  • 0代表资源没有被线程占用,资源现在是空闲状态
  • 1代表资源被线程占用,资源现在不是空闲状态

3.2 内部类Node(Node类在AQS类内部)

image-20230415112629037

image-20230415112746065

3.2.1 Node对象两种模式

  • SHARED(共享模式)
    • 标志节点正在等待共享模式的标记
  • EXCLUSIVE(独占模式)
    • 标志节点正在独占模式等待的标记

3.2.2 Node的Node的int变量waitState-volatile修饰

每个Node节点在AQS队列中的等待状态,Node初始化时waitState为0

  • CANCELLED(取消)=1
    • 表示该Node节点为取消状态,需要出队
  • SIGNAL(标志)=-1
    • 表示该节点的后一个节点需要unparking(LockSupport里的知识)
  • CONDITION(条件)=-2
    • 表示该节点正在等待某种条件激活
  • PROPAGATE(传播)=-3
    • 指示下一个 acquireShared 应该无条件传播的 waitStatus 值

只有CANCELLED状态是大于0的,判断时有需要先记住

3.2.3 prev&next 双向链表

记录该节点的前一个节点和后一个节点

3.3 以上得出结论:AQS同步队列的基本结构

CLH:Craig、Landin and Hagersten 队列,是个单向链表,AQS中的队列是CLH变体的虚拟双向队列(FIFO)

image-20230415113031991

4 AQS源码分析之ReentranLock

image-20230415132318457

4.1 架构

image-20230415115416750

上图可知:

  • ReentranLock实现了Lock接口
  • ReentranLock中的内部类NonfairSync(非公平锁)和FairSync(公平锁)继承了Sync
  • Sync又继承了AQS抽象类

4.2 ReentranLock公平锁与非公平锁

image-20230415120249600

上图可以得知:

  • new ReentranLock()和new ReentranLock(false)都是非公平锁
  • new ReentranLock(true) 表示公平锁

4.2.1 公平锁和非公平锁

4.2.1.1 公平锁和非公平锁的lock方法

提前了解acquire方法会调用tryAcquire,不管公平锁还是非公平锁最后都会调用acquire方法

image-20230415132026967

  • 因为非公平的的tryAcquire不讲武德,不排队所以我们在抢锁之前需要看看锁的状态status是否为0,不然队列中所有节点不管status是否为0都去强太耗费性能.
  • 而公平锁在tryAcquire的时候将武德,只有成为头结点才会去抢,所以直接acquire

image-20230415122841881

4.2.1.2 非公平锁的tryAcquire

非公平锁tryAcquire返回值:

  1. 当AQS status为0返回true
  2. 是重入锁的情况下返回true

image-20230415123058397

nonfairTryAcquire()方法

image-20230415124133044

4.2.1.3 公平锁的tryAcquire

公平锁tryAcquire返回值:

  1. 当AQS status为0并且当前节点是头结点时返回true
  2. 是重入锁的情况下返回true

image-20230415124400940

参考下图及结论:

!hasQueuedPredecessors()的意思就是如果在当前线程之前有排队的线程就不尝试去抢锁了,老老实实的排队,这不就是公平锁吗?

非公平锁没有这个条件约束,所以不管是排在那个位置的线程都会去抢一抢

hasQueuedPredecessors()方法

  • 如果在当前线程之前有排队的线程,则为true ;
  • 如果当前线程位于队列的头部或队列为空,则为false

image-20230415125536570

总结:

  • 可以明显看出公平锁与非公平锁的lock()方法唯一的区别就在于公平锁在获取同步状态时多了一个限制条件:
    hasQueuedPredecessors():
    hasQueuedPredecessors是公平锁加锁时判断等待队列中是否存在有效节点的方法

image-20230415125807397

4.3 以非公平锁为例继续后面的源码分析

tryAcquire方法在4.2章节以及分析后续我们主要针对

  • addWaiter
  • acquireQueued
  • selfInterrupt

以上三个方法进行分析

image-20230415132026967

4.3.0 acquire方法总流程

acquire

4.3.1 addWaiter(Node.EXCLUSIVE)

将加锁失败的节点加入队列

当tryAcquire()返回false的情况下才会执行addWaiter

  • 公平锁:

    以下条件只要包含一个就返回false

    1. status不等于0
    2. 当前节点不是头结点
    3. 也不是可重入锁
  • 非公平锁

    以下条件只要包含一个就返回false

    1. status不等于0
    2. 也不是可重入锁

image-20230415141207437

enq(Node node)

image-20230415140908057

4.3.2 acquireQueued(final Node node, int arg)

  • 将队列中各节点的等待状态waitStatus进行管理
    • waitStatus为-1的节点park
    • waitStatus为1的移出队列;
    • 其他状态值会逐步修改为-1,
    • 最后将waitStatus为-1的节点对应的线程park
  • 在addWaiter的大自旋中会不断tryAcquire(),检测共享资源是否解锁

addWaiter后会返回最新的尾节点

image-20230415145653986

predecessor()方法 返回尾结点的前置节点

image-20230415141624556

shouldParkAfterFailedAcquire(Node pred, Node node)

该方法主要作用就是将修改抢锁失败的节点waitStatus为-1等待状态,或者将想要取消的节点从队列中移除
如果前驱节点的 waitStatus 是 SIGNAL状态,即 shouldParkAfterFailedAcquire 方法会返回 true 程序会继续向下执行 parkAndCheckInterrupt 方法,用于将当前线程挂起

image-20230415145208528

image-20230415150527046

parkAndCheckInterrupt()

阻塞当前节点

image-20230415145734077

image-20230415150547678

4.3.3 selfInterrupt()

抢锁失败,加入队列成功那么就中断当前线程

image-20230415150048568

4.4 unlock解锁源码分析

解锁操作,主要作用是将头结点后为等待状态-1的节点unpark,然后唤醒阻塞的线程,以及一些异常处理

unlock主要就是调用AQS的释放方法release

image-20230415151000969

release开始会tryAcquire尝试解锁

image-20230415152139945

tryAcquire尝试解锁

image-20230415151810148

unparkSuccessor方法将中断节点恢复至正常状态

image-20230415154326651

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/430839.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Java 并发编程】一文读懂线程、协程、守护线程

一文读懂线程、协程、守护线程1. 线程的调度1.1 协同式线程调度1.2 抢占式线程调度1.3 设置线程的优先级2. 线程的实现模型和协程2.1 内核线程实现2.2 用户线程实现2.3 混合实现2.4 Java 线程的实现2.5 协程2.5.1 出现的原因2.5.2 什么是协程2.5.3 Java19 虚拟线程 - 协程的复苏…

Cat原理简析

Cat原理简析 链路追踪系统设计思路如何高效组织业务日志如何动态串联业务日志通用解决方案链路定义链路染色链路上报链路存储 Cat原理客户端原理API设计序列化和通信客户端埋点核心类分析流程分析启动流程:消息生产Context 线程本地变量Transaction事务的开启其他类型消息组合关…

网页设计方向有哪些SCI期刊推荐? - 易智编译EaseEditing

网页设计和开发方向主要涉及人机交互、用户体验、可访问性等方面,以下是几个相关的SCI期刊推荐: ACM Transactions on Computer-Human Interaction (ACM TOCHI): 该期刊由ACM(Association for Computing Machinery)出…

一次etcd变更引发的惨案

问题描述 在做etcd的数据变更时候,etcd在组成集群的时候出现leader不断切换问题,导致集群不稳定,都面将不健康的etcd节点踢出,只剩etcd单节点,后面将踢出的etcd节点重新加入现有etcd,导致etcd集群奔溃&…

【java踩坑搞起】MybatisPlus封装的mapper不支持 join,那咋办

众所周知,Mybatis Plus 封装的 mapper 不支持 join,如果需要支持就必须自己去实现。但是对于大部分的业务场景来说,都需要多表 join,要不然就没必要采用关系型数据库了。 直到前几天,偶然碰到了这么一款叫做mybatis-p…

权限提升:网站后台.(提权思路.)

权限提升:网站后台 权限提升简称提权,由于操作系统都是多用户操作系统,用户之间都有权限控制,比如通过 Web 漏洞拿到的是 Web 进程的权限,往往 Web 服务都是以一个权限很低的账号启动的,因此通过 Webshell …

Form Designer V2发布

基于Ant Design 和 jQuery UI 的表单设计器 github 地址 特性 React Vue 3.x Typescript 统一的组件定义,对Vue 和React 的实现提供一个统一的组件定义描述 概念 Component 组件Layout 布局,一种特殊的ComponentComponent Editor 组件属性编辑器Comp…

【开发日志】2023.04 ZENO----Composite----CompImport、ReadImageFlie

CompImport TEST: 用ParticlesWrangle创造属性A(紫色),B(青色) ,用CompImport结点将属性转化为图片输入到Composite3进行合成 Input: Output: /* 导入地形网格的属性,可能会有多个属…

Docker安装 docker-registry 镜像仓库

一、运行如下命令安装docker-registry镜像仓库: docker run -d \ -p 5000:5000 \ -v /usr/local/registry:/var/lib/registry \ --restartalways \ --name registry \ registry:2 二、测试镜像生成并推送到镜像仓库 1、新建一个目录 mkdir target 2、上传一…

Taro+Vue3 小程序引入echarts表

背景:根据需求在一个报告界面需要展示不同的echarts表来使数据更友好的显示。 效果如下: 一.taro支持echarts 官方说明:Taro 文档支持引用小程序端第三方组件库 物料文档:Taro 物料市场 | 让每一个轮子产生价值 二.引入echart…

Qt5.12實戰之Linux靜態庫編譯與調用完整過程

1.安裝gedit sudo apt-get install gedit -y 2.使用gedit編輯靜態庫源文件test.cpp gedit test.cpp 輸入下面內容 &#xff1a; #include <stdio.h> int func() {return 888; } 如下圖操作&#xff1a; 保存test.cpp並編譯 爲目標文件 gcc -c test.cpp如下圖示&am…

【剑指 Offer】(1)

文章目录前言一、 数组中重复的数字:fire: 解决方法:dog: 代码二、二维数组中的查找:fire:思路:dog:代码三、替换空格:fire:思路:dog: 代码四、从尾到头打印链表:fire:思路:dog:代码:dog: 代码五、重建二叉树:fire:思路:dog: 代码总结前言 剑指offer系列是一本非常著名的面试题…

【BUG SHOW】一个由高并发引起的缺陷分析

软件质量保障: 所寫即所思&#xff5c;一个阿里质量人对测试的所感所悟。 缺陷介绍 平台有这样的两个功能&#xff1a; ​功能01: 用户发起支付&#xff0c;成功则将表pay单据状态推进SUCCESS&#xff0c;然后发出支付结果消息&#xff1b;反之如果支付失败&#xff0c;则状态…

vue 报错 error:03000086:digital envelope routines::initialization error解决方案

目录 1. 引言: 2. 更换版本出现问题: 3. 出现原因: 4. 解决办法: -> 4. 1 删了 再换回16.15版本 -> 4.2 指令修改(好使) ---> 4.2.1效果如图 -> 4.3 其他指令就别试了 压根不好使 1. 引言: npm出现问题 , 卸载后 装了个新node 18.15版本 2. 更换版本…

Servlet-搭建个人博客系统(MVC架构模式简介,maven的配置和使用)

目录 1. MVC架构模式简介 2. maven的配置和使用 3. 项目总述&#x1f43b; 3.1 &#x1f34e;Controller层 3.2 &#x1f34e;Model层 3.3 &#x1f34e;View层 4. 页面的主要功能实现&#x1f43b; 4.1 &#x1f34e;登陆页面&#xff08;login.html&#xff09; 4.2…

Oracle Recovery Tools快速恢复断电引起的无法正常启动数据库----惜分飞

由于异常断电,数据库启动报错ORA-01113和ORA-01110&#xff0c;ORA-00322和ORA-00312以及ORA-00314和ORA-00312错误 Mon Apr 17 09:35:04 2023 ALTER DATABASE OPEN Errors in file D:\APP\ADMINISTRATOR\diag\rdbms\orcl\orcl\trace\orcl_ora_10192.trc: ORA-01113: 文件 1 需…

史上最牛二分查找,不服来战

&#x1f929;本文作者&#xff1a;大家好&#xff0c;我是paperjie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 &#x1f970;内容专栏&#xff1a;这里是《算法详解》&#xff0c;笔者用重金(时间和精力)打造&#xff0c;将算法知识一网打尽&#xff0c;希望可以…

音视频-ffplay的音视频同步

最近自己在做一个视频播放器&#xff0c;渲染视频帧时有些疑惑&#xff0c;所以特意来学习一下ffplay中是如何处理视频帧的渲染的&#xff01;&#x1f60a; 我自己最初的理解是这样 1、只关心视频本身的时间戳&#xff0c;不考虑音视频同步以及其他的同步时钟&#xff0c;或…

写了那么久的文章,现在才改回来!

大家好&#xff0c;我是即兴小索奇&#xff0c;最近在阅读文章时发现了自己文章的一个缺陷&#xff0c;就记录下来并分享给大家&#xff0c;大家写文章时也可以借鉴。 这是我以前写的的文章&#xff0c;light亮色下显示正常 -当我不经意间把手机调成深色模式阅读文章时&#xf…

Leetcode912.排序数组(三路划分)

文章目录 一、三路划分二、Leetcode912.排序数组 一、三路划分 为何还会有三路划分&#xff1f; 快速排序算法在某个数据大量重复时效率极低&#xff0c;在运行程序时会超出时间限制&#xff0c;为了解决数据大量重复的情况下&#xff0c;三路划分诞生了。三路划分是基于快速排…