提升--09-1--AQS底层逻辑实现

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 一、怎么解释AQS是什么?
    • ==AQS的本质是JUC包下一个抽象类,AbstractQueuedSynchronizer (抽象的队列式同步器)==
  • 二、AQS核心底层和Lock是什么关系?
    • ReentrantLock的互斥锁功能就是基于AQS实现的。
    • 优先聊一下lock方法的区别。
    • 分析一下acquire方法中做了什么事
  • 三、AQS如何尝试获取资源?
    • 非公平锁的tryAcquire实现
    • 公平锁的实现
  • 四、AQS获取资源失败如何排队?
  • 五、AQS排队后如何重新尝试获取资源?
  • 六、AQS如何释放资源?


一、怎么解释AQS是什么?

AQS的本质是JUC包下一个抽象类,AbstractQueuedSynchronizer (抽象的队列式同步器)

  • AQS是JUC下的一个基础类,目的是为了提供一个共性的功能,让其他类去继承,比如ReentrantLock,CountDownLatch,Semaphore,ThreadPoolExecutor…………
    在这里插入图片描述在这里插入图片描述

二、AQS核心底层和Lock是什么关系?

ReentrantLock的互斥锁功能就是基于AQS实现的。

通过源码可以看到,ReentrantLock类并没有直接继承AQS,而是ReentrantLock的内部类Sync继承了AQS。

在这里插入图片描述

Sync也是一个抽象类,他下面有两个实现。一个FairSync,一个NonfairSync。一个公平锁,一个非公平锁。

在这里插入图片描述

在这里插入图片描述
在ReentrantLock中,公平锁和非公平锁对于lock方法和tryAcquire方法的实现是不同的。

优先聊一下lock方法的区别。

// 非公平锁的lock方法
final void lock() {// 不管是否有线程在持有锁资源,直接尝试将state从0改为1,尝试拿锁。if (compareAndSetState(0, 1))// 拿锁成功了。将当前线程设置到exclusiveOwnerThreadsetExclusiveOwnerThread(Thread.currentThread());else// 前面抢锁失败走acquireacquire(1);
}// 公平锁的lock方法
final void lock() {acquire(1);
}

分析一下acquire方法中做了什么事

// acquire方法实现
public final void acquire(int arg) {//1、tryAcquire方法:尝试获取锁资源的过程。拿到锁返回true,反之返回false。// 没拿到锁,才会走2和3。//2、addWaiter方法: 将当前没拿到锁的线程封装为Node,添加到同步队列。//3、acquireQueued方法: 长时间等待需要挂起线程,并且等到线程排到第一名时,//                      需要再次尝试获取锁资源if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();
}

三、AQS如何尝试获取资源?

tryAcquire方法是如何尝试获取锁资源的,tryAcquire方法有两种实现,一种是公平,一种是非公平。

  • 判断state为0,那就根据情况抢锁
  • state不为0,但是当前线程持有锁,那就走锁重入的逻辑
  • 前面都不满足,告辞!

非公平锁的tryAcquire实现

// 非公平锁的实现。
final boolean nonfairTryAcquire(int acquires) {// 拿到当前线程。final Thread current = Thread.currentThread();// 获取stateint c = getState();// 判断state是否为0if (c == 0) {// 当前没有线程持有锁。非公平锁直接尝试抢锁,抢成功就返回trueif (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}// 有线程持有锁。那就判断持有锁的线程是不是当前线程。else if (current == getExclusiveOwnerThread()) {// 到这说明是锁重入操作。// 将state + 1int nextc = c + acquires;// 判断+1之后,如果小于0,说明超过int正整数的取值范围了,无法再次重入~if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");// 将 + 1后的值赋值给statesetState(nextc);// 返回true,锁重入成功~return true;}//没拿到锁,返回falsereturn false;
}

公平锁的实现

// 公平锁的实现。
protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {// 公平锁这里多了一行hasQueuedPredecessors()// 如果没有线程持有锁资源,优先查看是否有排队的线程// 1、如果没有线程排队,返回false,代表可以抢锁。// 2、如果有排队的,但是当前线程排在“第一名”,返回false,代表可以抢锁。// 3、如果有排队的,但是当前线程没有排在“第一名”,返回true,代表不可以抢锁。if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;a}}else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0)throw new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;
}

四、AQS获取资源失败如何排队?

如果获取锁资源失败,会执行addWaiter方法,去做排队操作。

  1. 将当前线程封装Node。
  2. 如果同步队列为null,需要优先初始化一个虚拟的Node节点
  3. 将当前Node添加到tail的后面。
private Node addWaiter(Node mode) {// 将当前线程封装NodeNode node = new Node(Thread.currentThread(), mode);// 拿到尾结点Node pred = tail;// 不为null,代表现在有Node对象。if (pred != null) {node.prev = pred;if (compareAndSetTail(pred, node)) {pred.next = node;return node;}}// 如果pred是null,enq(node);return node;
}
// 将node添加到同步队列
private Node enq(final Node node) {// 死循环是为了确保一定能添加成功for (;;) {Node t = tail;if (t == null) { // Must initialize// 初始化一个没有线程信息的Node,作为头尾if (compareAndSetHead(new Node()))tail = head;} else {node.prev = t;if (compareAndSetTail(t, node)) {t.next = node;return t;}}}
}

五、AQS排队后如何重新尝试获取资源?

  • 挂起需要确保prev节点的状态为-1。
  • 重新获取锁资源需要确保当前node是head.next,并且基于tryAcquire去获取锁资源。
// 排队后的挂起操作和获取锁资源的操作
// node就是刚刚去排队的Node
final boolean acquireQueued(final Node node, int arg) {// 拿锁失败了么??trueboolean failed = true;try {// 死循环,拿到锁才能走!!for (;;) {// 拿到当前Node的上一个Nodefinal Node p = node.prev;// 只有head.next的node才有资格抢锁if (p == head && tryAcquire(arg)) {// 说明拿到锁资源了。// 当前node成为新的head,线程和prev都设置为nullsetHead(node);p.next = null; failed = false;// 拿到成功,返回中断标记位(这里省略了这部分代码)return false;}// 没资格拿,或者没拿到!// 需要优先掌握一个知识,Node中有一个waitStatus的状态// 1:代表当前Node取消了,不排了,告辞,走人。// 0:代表默认状态,啥事没有~// -1:代表当前Node的next节点可能挂起了。if (shouldParkAfterFailedAcquire(p, node) &&// 基于Unsafe类的park方法,将当前线程挂起!parkAndCheckInterrupt())}} finally {if (failed)cancelAcquire(node);}
}private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {// pred是prev// node是curr// 拿到上一个Node的状态int ws = pred.waitStatus;// 如果上一个Node状态是-1,返回true,代表可以挂起if (ws == Node.SIGNAL)return true;// 上一个节点状态是否是取消状态if (ws == 1) {// 绕过状态为1的节点,找到一个状态正常的。do {node.prev = pred = pred.prev;} while (pred.waitStatus == 1);pred.next = node;} else {// 如果上一个节点状态正常,直接修改为-1compareAndSetWaitStatus(pred, ws, Node.SIGNAL);}return false;
}

六、AQS如何释放资源?

  • 加锁是执行lock,释放锁资源玩的是unlock方法。
  • 释放锁会对state做-1操作,如果-1后state为0,代表释放锁资源成功。
  • 如果锁资源释放成功,需要查看head状态是否为-1,如果为-1需要唤醒离head最近的有效节点。
// 释放锁资源
public final boolean release(int arg) {// 执行tryAcquire释放锁资源if (tryRelease(arg)) {// 释放干净了!// 先拿headNode h = head;// 如果head不为null,head的状态是否为 -1if (h != null && h.waitStatus == -1)// 唤醒后面挂起的线程(睡觉的线程)unparkSuccessor(h);return true;}return false;
}
// 释放锁操作。
protected final boolean tryRelease(int releases) {int c = getState() - releases;// 释放锁资源的线程必须是持有锁资源的线程,否则甩你一个异常。if (Thread.currentThread() != getExclusiveOwnerThread())throw new IllegalMonitorStateException();// free代表锁释放干净了么?boolean free = false;// 如果state为0,代表锁资源释放干净了// 没进if,就代表没释放干净if (c == 0) {free = true;setExclusiveOwnerThread(null);}setState(c);return free;
}// 唤醒后续挂起的线程   node是head
private void unparkSuccessor(Node node) {// 拿到head的状态int ws = node.waitStatus;// 状态是-1,归位0if (ws < 0)compareAndSetWaitStatus(node, ws, 0);// s可能就是要被唤醒的线程Node s = node.next;// 如果s节点出现了问题,不排队了,那就找离head最近的有效节点唤醒!if (s == null || s.waitStatus > 0) {s = null;// 会从tail开始往前找,找到离head最近的有效节点for (Node t = tail; t != null && t != node; t = t.prev)if (t.waitStatus <= 0)s = t;}// 如果s不为null,代表找到了具体要唤醒的Nodeif (s != null)// 唤醒对应的线程LockSupport.upnark(s.thread);
}

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

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

相关文章

四川芸鹰蓬飞:抖店运营的时候注意什么?

抖店作为一个短视频平台&#xff0c;吸引了越来越多的商家加入。在抖店上进行有效的运营是提高销量和曝光度的关键。那么&#xff0c;抖店怎么设置运营呢&#xff1f;有哪些方法可以帮助商家在这个竞争激烈的平台上脱颖而出呢&#xff1f; 一、抖店怎么设置运营&#xff1f; 首…

微服务实战系列之加密RSA

前言 在这个时代&#xff0c;我们选择的人生目标已丰富多彩&#xff0c;秉持的人生态度也千差万别&#xff1a; 除了吃喝玩乐&#xff0c;还有科技探索&#xff1b; 除了CityWalk&#xff0c;还有“BookWalk”&#xff1b; 除了走遍中国&#xff0c;还有走遍世界&#xff1b; …

Selenium自动化测试详解

最近也有很多人私下问我&#xff0c;selenium学习难吗&#xff0c;基础入门的学习内容很多是3以前的版本资料&#xff0c;对于有基础的人来说&#xff0c;3到4的差别虽然有&#xff0c;但是不足以影响自己&#xff0c;但是对于没有学过的人来说&#xff0c;通过资料再到自己写的…

Spring-IOC-Spring6和JUnit5集成

1、父工程pom.xml <properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><spring.version>…

【FLink】水位线(Watermark)

目录 1、关于时间语义 1.1事件时间 1.2处理时间​编辑 2、什么是水位线 2.1 顺序流和乱序流 2.2乱序数据的处理 2.3 水位线的特性 3 、水位线的生成 3.1 生成水位线的总体原则 3.2 水位线生成策略 3.3 Flink内置水位线 3.3.1 有序流中内置水位线设置 3.4.2 断点式…

【shell】条件语句

一、测试 1.1文件测试test test命令是内部命令 test的语法 test 条件表达式 [ 条件表达式 ] test 选项 文件 -d &#xff1a;判断是否是目录 -f &#xff1a;判断是否是普通文件 -b &#xff1a;判断是否是块设备 -c &#xff1a;判断是否是字符设备 -e &#xff1a;判断是否…

【Python】给出一个包含n个整数的数列,问整数a在数列中的第一次出现是第几个。

问题描述 给出一个包含n个整数的数列&#xff0c;问整数a在数列中的第一次出现是第几个。 输入格式 第一行包含一个整数n。 第二行包含n个非负整数&#xff0c;为给定的数列&#xff0c;数列中的每个数都不大于10000。 第三行包含一个整数a&#xff0c;为待查找的数。 输出格式…

rv1126-rv1109-openssh

这是一个工具&#xff0c;可以通过ssh远程登录来操作&#xff0c;非常逆天&#xff01; 于是rv1109代码自身自带有openssh 所以只需要打开config即可 diff --git a/buildroot/configs/rockchip_rv1126_rv1109_spi_nand_defconfig b/buildroot/configs/rockchip_rv1126_rv1109…

Android codec2 视频框架之输出端的内存管理

文章目录 前言setSurfacestart从哪个pool中申请buffer解码后框架的处理流程renderOutbuffer 输出显示 前言 输出buffer整体的管理流程主要可以分为三个部分&#xff1a; MediaCodc 和 应用之间的交互 包括设置Surface、解码输出回调到MediaCodec。将输出buffer render或者rele…

手机照片一键去水印轻松摆脱不需要的旁观者

是什么让照片中的意外客人成为挑战&#xff1f;我们都经历过这种情况——在热门地标或繁忙的城市街道拍照&#xff0c;不可避免地会在画面中捕捉到陌生人。有时他们会无意中抢尽风头&#xff0c;转移观众的注意力。 这些水印不仅影响了照片的美观度&#xff0c;还给我们的观赏体…

京东数据分析软件(京东平台数据分析):2023年Q3扫地机器人行业消费报告

随着90后、00后逐渐成为消费主力军&#xff0c;他们对生活品质更加关注、健康意识进一步增强&#xff0c;再加上“懒人经济”的盛行&#xff0c;人们对扫地机器人的使用率和关注热情也不断增长。 根据鲸参谋电商数据分析平台的相关数据显示&#xff0c;今年7月份-9月份&#xf…

AI大爆发的时代,未来的年轻人怎样获得机会和竞争力?

文章目录 引言AI与教育工作者教育资源不平衡 这次&#xff0c;狼真的来了。 引言 AI正迅猛地改变着我们的生活。 根据高盛发布的一份报告&#xff0c;AI有可能取代3亿个全职工作岗位&#xff0c;影响全球18%的工作岗位。在欧美&#xff0c;或许四分之一的工作可以用AI完成。另…

第四代智能井盖传感器,更迭智能井盖监测办法

人工检查井盖是一项耗时且效率低下的工作&#xff0c;需要工作人员逐个进行检查。由于这种方式无法实时监测井盖的状态&#xff0c;当井盖出现故障时无法及时将信息反馈给相关人员&#xff0c;从而影响了井盖的维修效率。此外人工检查还受到天气、光线等环境因素的影响较大&…

卷积神经网络(ResNet-50)鸟类识别

文章目录 卷积神经网络&#xff08;CNN&#xff09;mnist手写数字分类识别的实现卷积神经网络&#xff08;CNN&#xff09;多种图片分类的实现卷积神经网络&#xff08;CNN&#xff09;衣服图像分类的实现卷积神经网络&#xff08;CNN&#xff09;鲜花的识别卷积神经网络&#…

基于社交网络算法优化概率神经网络PNN的分类预测 - 附代码

基于社交网络算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于社交网络算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于社交网络优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神…

一、MySQL-Replication(主从复制)

1.1、MySQL Replication 主从复制&#xff08;也称 AB 复制&#xff09;允许将来自一个MySQL数据库服务器&#xff08;主服务器&#xff09;的数据复制到一个或多个MySQL数据库服务器&#xff08;从服务器&#xff09;。 根据配置&#xff0c;您可以复制数据库中的所有数据库&a…

Flowable工作流基础篇

文章目录 一、Flowable介绍二、Flowable基础1.创建ProcessEngine2.部署流程定义3.启动流程实例4.查看任务5.完成任务6.流程的删除7.查看历史信息 三、Flowable流程设计器1.Eclipse Designer1.1 下载安装Eclipse1.2 安装Flowable插件1.3 创建项目1.4 创建流程图1.5 部署流程 2.F…

Maven工程继承关系,多个模块要使用同一个框架,它们应该是同一个版本,项目中使用的框架版本需要统一管理。

1、父工程pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/PO…

KMP——字符串匹配

朴素匹配的逻辑&#xff1a; 将原串的指针移动至本次发起点的下一个位置&#xff08;b字符处&#xff09;&#xff1b;匹配串的指针移动至起始位置。尝试匹配&#xff0c;发现对不上&#xff0c;原串的指针会一直往后移动&#xff0c;直到能够与匹配串对上位置。 如图&#x…

(02)vite环境变量配置

文章目录 将开发环境和生产环境区分开环境变量vite处理环境变量loadEnv 业务代码需要使用环境变量.env.env.development.env.test修改VITE_前缀 将开发环境和生产环境区分开 分别创建三个vite 的配置文件&#xff0c;并将它们引入vite.config.js vite.base.config.js import…