提升--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.rhkb.cn/news/200397.html

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

相关文章

【Python】给出n个数,找出这n个数的最大值,最小值,和。

问题描述 给出n个数&#xff0c;找出这n个数的最大值&#xff0c;最小值&#xff0c;和。 样例输入 5 1 3 -2 4 5 Data 样例输出 5 -2 11 n int(input()) # 从用户输入中读取一个整数&#xff0c;将其赋给变量n# 从用户输入中读取一行字符串&#xff0c;使用空格分割字符串&a…

LangChain 4用向量数据库Faiss存储,读取YouTube的视频文本搜索Indexes for information retrieve

接着前面的Langchain&#xff0c;继续实现读取YouTube的视频脚本来问答Indexes for information retrieve LangChain 实现给动物取名字&#xff0c;LangChain 2模块化prompt template并用streamlit生成网站 实现给动物取名字LangChain 3使用Agent访问Wikipedia和llm-math计算狗…

Python武器库开发-flask篇之error404(二十七)

flask篇之error404(二十七) 首先&#xff0c;我们先进入模板的界面创建一个404的html页面 cd templates vim 404.html404.html的内容如下&#xff1a; <h1>error!!!</h1>在 Flask 应用程序中&#xff0c;当用户访问一个不存在的页面的时候&#xff0c;会出现 4…

C进阶---动态内存管理

目录 一、为什么存在动态内存分配 1.1静动态内存分配区别&#xff1a; 1.2静态分配的优缺点 1.3动态分配优缺点 二、动态内存函数的介绍 2.1malloc和free 2.2calloc 2.3realloc 三、常见的动态内存错误 3.1对NULL指针的解引用操作 3.2 对动态开辟空间的越界…

Java实现拼图小游戏

1、了解拼图游戏基本功能&#xff1a; 拼图游戏内容由若干小图像块组成的&#xff0c;通过鼠标点击图像块上下左右移动&#xff0c;完成图像的拼凑。 2、拼图游戏交互界面设计与开发&#xff1a; 通过创建窗体类、菜单、中间面板和左右面板完成设计拼图的交互界面 &#xff…

14. UART串口通信

14. UART串口通信 1. UART1.1 UART 通信格式1.2 UART 电平标准1.3 I.MX6U UART 简介1.3.1 控制寄存器1 UARTx_UCR1(x1~8)1.3.2 控制寄存器2 UARTx_UCR21.3.3 控制寄存器3 UARTx_UCR31.3.4 状态寄存器2 UARTx_USR21.3.4 UARTx_UFCR 、 UARTx_UBIR 和 UARTx_UBMR1.3.5 UARTx_URXD…

P2 C++变量

前言 一 C变量的作用 本期我们来讨论一下c 中的变量。 在一个 C 程序中&#xff0c;大部分内容实际上都是在使用数据。我们操作任何类型的数据&#xff0c;如包括我们想要改变、想要修改&#xff0c; 想要读和写数据。我们都需要把数据存储进叫做变量的东西里面。变量允许我们…

Activiti,Apache camel,Netflex conductor对比,业务选型

Activiti,Apache camel,Netflex conductor对比&#xff0c;业务选型 1.activiti是审批流&#xff0c;主要应用于人->系统交互&#xff0c;典型应用场景&#xff1a;请假&#xff0c;离职等审批 详情可见【精选】activti实际使用_activiti通过事件监听器实现的优势_记录点滴…

【开发流程】持续集成、持续交付、持续部署

一、开发工作流程 假设把开发流程分为以下几个阶段&#xff1a; 编码 -> 构建 -> 集成 -> 测试 -> 交付 -> 部署 如上图所示&#xff0c;持续集成、持续交付、持续部署有着不同的软件自动交付周期。 二、持续集成、持续交付、持续部署 1、持续集成 持续集成…

mysql 查询

-- 多表查询select * from tb_dept,tb_emp; 内来链接 -- 内连接 -- A 查询员工的姓名 &#xff0c; 及所属的部门名称 &#xff08;隐式内连接实现&#xff09;select tb_emp.name,tb_dept.name from tb_emp,tb_dept where tb_emp.idtb_emp.id;-- 推荐使用select a.name,b.n…

SSL加密

小王学习录 今日摘录前言HTTP + SSL = HTTPSSSL加密1. 对称加密2. 非对称加密 + 对称加密3. 证书今日摘录 但愿四海无尘沙,有人卖酒仍卖花。 前言 SSL表示安全套接层,是一个用于保护计算机网络中数据传输安全的协议。SSL通过加密来防止第三方恶意截取并篡改数据。在实际应用…

GaussDB新特性Ustore存储引擎介绍

1、 Ustore和Astore存储引擎介绍 Ustore存储引擎&#xff0c;又名In-place Update存储引擎&#xff08;原地更新&#xff09;&#xff0c;是openGauss 内核新增的一种存储模式。此前的版本使用的行存储引擎是Append Update&#xff08;追加更新&#xff09;模式。相比于Append…

泉盛UV-K5/K6全功能中文固件

https://github.com/wu58430/uv-k5-firmware-chinese/releases 主要功能&#xff1a; 中文菜单 许多来自 OneOfEleven 的模块&#xff1a; AM 修复&#xff0c;显著提高接收质量长按按钮执行 F 操作的功能复制快速扫描菜单中的频道名称编辑频道名称 频率显示选项扫描列表分配…

Java入门篇 之 内部类

本篇碎碎念&#xff1a;本篇没有碎碎念&#xff0c;想分享一段话&#xff1a; 你不笨&#xff0c;你只是需要时间&#xff0c;耐心就是智慧&#xff0c;不见得快就好&#xff0c;如果方向都不对&#xff0c;如果心术不正&#xff0c;如果德不配位&#xff0c;快就是对自己天分的…

easyExcel注解详情

前言11个注解字段注解 类注解基础综合示例补充颜色总结 11个注解 ExcelProperty ColumnWith 列宽 ContentFontStyle 文本字体样式 ContentLoopMerge 文本合并 ContentRowHeight 文本行高度 ContentStyle 文本样式 HeadFontStyle 标题字体样式 HeadRowHeight 标题高度 HeadStyle…

8Base集团通过SmokeLoader部署新的Phobos勒索软件变种

最近&#xff0c;8Base集团的威胁行为者通过Phobos勒索软件的变种展开了一系列金融动机的攻击。这一发现来自于思科Talos的研究结果&#xff0c;他们记录了网络犯罪分子活动的增加。 安全研究员Guilherme Venere在周五发表的详尽的两部分分析中表示&#xff1a;“该组织的大多…

excel导入 Easy Excel

依旧是框架感觉有东西&#xff0c;但是确实是模拟不出来&#xff0c;各种零零散散的件太多了 controller层 ApiOperation(value "导入Excel", notes "导入Excel", httpMethod "POST", response ExcelResponseDTO.class)ApiImplicitParams({…

如何有效的禁止Google Chrome自动更新?

禁止Chrome自动更新 1、背景2、操作步骤 1、背景 众所周知&#xff0c;当我们在使用Selenium进行Web自动化操作&#xff08;如爬虫&#xff09;时&#xff0c;一般会用到ChromeDriver。然而Driver的更新速度明显跟不上Chrome的自动更新。导致我们在使用Selenium进行一些操作时就…

华为ac+fit无线2层漫游配置案例

ap的管理dhcp在ac上&#xff0c;业务dhcp在汇聚交换机上、并且带2层漫游 R1: interface GigabitEthernet0/0/0 ip address 11.1.1.1 255.255.255.0 ip route-static 12.2.2.0 255.255.255.0 11.1.1.2 ip route-static 192.168.0.0 255.255.0.0 11.1.1.2 lsw1: vlan batch 100…

鸿蒙系统扫盲(二):再谈鸿蒙是不是安卓套壳?

最近小米发布了澎湃OS&#xff0c;vivo发布了蓝OS&#xff0c;好像自从华为回归后&#xff0c;大伙都开始写自己的OS了&#xff0c;小米官方承认是套壳安卓&#xff0c;然后被大家喷了&#xff0c;于是鸿蒙是不是安卓套壳的话题又回到了大众的视野&#xff0c;今天在讨论下这个…