AQS机制详解

案例一

public class AqsThread extends Thread {private Lock lock;public AqsThread(String name, Lock lock) {super(name);this.lock = lock;}@Overridepublic void run() {lock.lock();try {System.out.println(Thread.currentThread().getName() + "running");} finally {lock.unlock();}}
}
public class AqsDemo {public static void main(String[] args) {ReentrantLock lock = new ReentrantLock();AqsThread t1 = new AqsThread("t1", lock);AqsThread t2 = new AqsThread("t2", lock);t1.start();t2.start();}
}

执行案例的运行结果.(其中之一) 

 

线程t1先执行lock操作,获取锁.然后线程t2执行lock操作.然后t1进行unlock操作,然后t2获取锁成功,接着执行unlock操作.

 t1线程调用lock.lock方法,方法调用顺序.

t2线程调用lock.lock方法,其方法调用顺序. 

最后的结果是被阻塞.源码如下.

 

t1线程调用lock.unlock,其方法调用顺序.

t1线程中调用lock.unlock后,经过一系列的调用,最终的状态是释放了许可,因为调用了LockSupport.unpark。这时,t2线程就可以继续运行了。此时,会继续恢复t2线程运行环境,继续执行LockSupport.park后面的语句.

 进一步调用.

 

t2线程调用lock.unlock,其方法调用顺序 

 

t2线程执行lock.unlock后,最终达到的状态还是与之前的状态一样.

案例二 

public class DepotDemo {private int size;private int capacity;private Lock lock;private Condition fullCondition;private Condition emptyCondition;public DepotDemo(int capacity) {this.capacity = capacity;lock = new ReentrantLock();fullCondition = lock.newCondition();emptyCondition = lock.newCondition();}public void produce(int no) {int left = no;lock.lock();try {while (left > 0) {while (size >= capacity)  {System.out.println(Thread.currentThread().getName() + " before await");fullCondition.await();System.out.println(Thread.currentThread().getName() + " after await");}int inc = (left + size) > capacity ? (capacity - size) : left;left -= inc;size += inc;System.out.println("produce = " + inc + ", size = " + size);emptyCondition.signal();}} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}public void consume(int no) {int left = no;lock.lock();try {while (left > 0) {while (size <= 0) {System.out.println(Thread.currentThread() + " before await");emptyCondition.await();System.out.println(Thread.currentThread() + " after await");}int dec = (size - left) > 0 ? left : size;left -= dec;size -= dec;System.out.println("consume = " + dec + ", size = " + size);fullCondition.signal();}} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}
}
public class ReentrantLockTest {static class Consumer {private DepotDemo depot;public Consumer(DepotDemo depot) {this.depot = depot;}public void consume(int no) {new Thread(new Runnable() {@Overridepublic void run() {depot.consume(no);}}, no + " consume thread").start();}}static class Producer {private DepotDemo depot;public Producer(DepotDemo depot) {this.depot = depot;}public void produce(int no) {new Thread(new Runnable() {@Overridepublic void run() {depot.produce(no);}}, no + " produce thread").start();}}}class ReentrantLockDemo {public static void main(String[] args) throws InterruptedException {DepotDemo depot = new DepotDemo(500);new ReentrantLockTest.Producer(depot).produce(500);new ReentrantLockTest.Producer(depot).produce(200);new ReentrantLockTest.Consumer(depot).consume(500);new ReentrantLockTest.Consumer(depot).consume(200);}
}

 

通过静态分析.可以得出如下调用流程.

 

p1线程调用lock.lock,获得锁,继续运行. p2线程调用lock.lock,条件不满足,被放入条件队列阻塞等待唤醒.

c1线程调用lock.lock.

 

c2线程调用lock.lock.

 

通过上面的分析.阻塞的时候会释放锁进入条件队里里等待. 被唤醒后会重新进入抢锁队里.

p1线程执行emptyCondition.signal,其方法调用顺序.

 

p1线程执行lock.unlock,示意图如下.

 

p2成为头节点.获取到锁继续执行.c1和c2还处于阻塞状态. 

p2线程执行fullCondition.await,其方法调用顺序.

新生成一个节点放入到条件队列,并且释放锁.

 

 

继续运行c1线程,c1线程由于之前被park了,所以此时恢复,继续之前的步骤,即还是执行前面提到的acquireQueued方法,之后,c1判断自己的前驱结点为head,并且可以获取锁资源. 

 

c1线程执行fullCondtion.signal,方法调用顺序 .

signal方法达到的最终结果是将包含p2线程的结点从condition queue中转移到sync queue中,之后condition queue为null,之前的尾结点的状态变为SIGNAL。

 

 

c1线程执行lock.unlock操作,根据之前的分析,经历的状态变化.

  

c2线程执行emptyCondition.await

 

await操作将会生成一个结点放入condition queue中与之前的一个condition queue是不相同的,并且unpark头节点后面的结点,即包含线程p2的结点。

p2线程被unpark,故可以继续运行,经过CPU调度后,p2继续运行,之后p2线程在AQS:await方法中被park,继续AQS.CO:await方法的运行.

 

p2继续运行,执行emptyCondition.signal.

 

最终,将condition queue中的结点转移到sync queue中,并添加至尾部,condition queue会为空,并且将head的状态设置为SIGNAL。

p2线程执行lock.unlock操作,根据前面的分析可知.

 

unlock操作会释放c2线程的许可,并且将头节点设置为c2线程所在的结点。

整个流程就结束了.只要仔细研究AQS源码.这些运用基本上会很好理解.多读多看.每一次重复的看,都会有新的理解.

知行合一,先知,知到行,最后知行合一.

如果大家喜欢我的分享的话,可以关注下我的微信公众号

心有九月星辰 

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

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

相关文章

【LeetCode】每日一题 2024_10_5 完成旅途的最少时间(二分答案)

前言 每天和你一起刷 LeetCode 每日一题~ 大家国庆节快乐呀~ LeetCode 启动&#xff01; 突然发现&#xff0c;国庆的每日一题&#xff0c;不是坐公交就是坐火车&#xff0c;不是坐火车就是做飞机&#xff0c;这就是你的国庆旅游计划吗&#xff01;力扣&#xff01; 题目&a…

图表不会做怎么办?AI一键生成好看图表!

本期教你如何用AI一键生成各种数据图表&#xff01; 本文阅读难度&#xff1a;★☆☆☆☆ 看看别人做的这些图表&#xff0c;是不是挺好看的&#xff1f; 特别是作为接商单的新写手&#xff0c;看到这些&#xff0c;头都大了&#xff0c;该怎么办呢&#xff1f; 不用怕&…

ModuleNotFoundError: No module named ‘package‘

报错&#xff1a; Traceback (most recent call last): File “”, line 198, in run_module_as_main File “”, line 88, in run_code File "D:\python\helloworld.venv\Scripts\pip.exe_main.py", line 4, in File "D:\python\helloworld.venv\Lib\site-pac…

MAC备忘录空白解决方案

打开icloud->备忘录 取消勾选同步此MAC后再次勾选&#xff0c;然后点击完成即可。

S7-200 SMART的数据类型说明

S7-200 SMART的数据主要分为&#xff1a; 与实际输入/输出信号相关的输入/输出映象区&#xff1a; I&#xff1a;数字量输入&#xff08;DI&#xff09;Q&#xff1a;数字量输出&#xff08;DO&#xff09;AI&#xff1a;模拟量输入AQ&#xff1a;模拟量输出 内部数据存储区…

NVIDIA网卡系列之ConnectX-4规格信息(50G-PCIe 3.0x8-8PF256VF-2015年发布)

背景 NVIDIA ConnectX-4系列的网卡&#xff0c;早期还在Mellanox未被NVIDIA收购的时候就发布了&#xff0c;支持50G&#xff0c;PCIe3.0&#xff0c;最大x8通道lanes。 是50G级别的一代&#xff08;10G-CX3&#xff0c;50G-CX4&#xff0c;100G-CX5&#xff0c;200G-CX6&#…

基于Python的自然语言处理系列(24):BiDAF(双向注意力流)

在自然语言处理领域,机器阅读理解(Machine Comprehension, MC)是一个重要的任务。在这篇博文中,我们将实现论文 BiDAF 中提出的双向注意力流模型。BiDAF 主要改进了传统注意力机制中的早期信息摘要问题,并引入了字符嵌入来加强对单词细粒度信息的理解。 1. 加载 SQuAD 数据…

ThreadLocal底层原理及数据结构详解

ThreadLocal允许为每个线程创建独立的变量副本&#xff0c;使得同一个ThreadLocal对象在不同的线程中拥有不同的值。它的主要作用是在并发环境下提供线程隔离&#xff0c;避免多个线程共享同一个变量&#xff0c;从而减少线程间的相互干扰。 ThreadLocal的核心在于为每个线程维…

【案例】距离限制模型透明

开发平台&#xff1a;Unity 2023 开发工具&#xff1a;Unity ShaderGraph   一、效果展示 二、路线图 三、案例分析 核心思路&#xff1a;计算算式&#xff1a;透明值 实际距离 / 最大距离 &#xff08;实际距离 ≤ 最大距离&#xff09;   3.1 说明 | 改变 Alpha 值 在 …

【JAVA开源】基于Vue和SpringBoot的服装生产管理系统

本文项目编号 T 066 &#xff0c;文末自助获取源码 \color{red}{T066&#xff0c;文末自助获取源码} T066&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

git diff 查看到一行变动,但是目测无差异怎么办?

1. 目测无变化 直接用 git diff main.js 提示有一行变动&#xff0c;但是目测看不出来差异。 结果如图&#xff1a;up panel. 2. 大概是空格的问题&#xff0c;使用参数 --ws-error-highlightall $ git diff --ws-error-highlightall main.js结果如图: down panel.

黑神话:仙童,数据库自动反射魔法棒

黑神话&#xff1a;仙童&#xff0c;数据库自动反射魔法棒 Golang 通用代码生成器仙童发布了最新版本电音仙女尝鲜版十一及其介绍视频&#xff0c;视频请见&#xff1a;https://www.bilibili.com/video/BV1ET4wecEBk/ 此视频介绍了使用最新版的仙童代码生成器&#xff0c;将 …

Llama 3.2 微调指南

让我们通过微调 Llama 3.2 来找到一些精神上的平静。 我们需要安装 unsloth&#xff0c;以更小的尺寸实现 2 倍的快速训练 !pip install unsloth!pip uninstall unsloth -y && pip install --upgrade --no-cache-dir "unsloth[colab-new] githttps://github.co…

Spring Boot技术在大学生就业服务中的应用

1系统概述 1.1 研究背景 如今互联网高速发展&#xff0c;网络遍布全球&#xff0c;通过互联网发布的消息能快而方便的传播到世界每个角落&#xff0c;并且互联网上能传播的信息也很广&#xff0c;比如文字、图片、声音、视频等。从而&#xff0c;这种种好处使得互联网成了信息传…

视频格式批量转换:一键操作,轻松搞定

在处理大量视频文件时&#xff0c;格式转换是一个常见需求&#xff0c;不同的平台和设备对视频格式的要求各不相同&#xff0c;批量转换视频格式能显著提高工作效率。帮助大家轻松应对各种视频格式转换难题。 1.在“视频剪辑高手”的功能选项里切换到“批量转换视频”版块上 2.…

大学生就业服务:Spring Boot技术实践

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常适…

C++结构体定义和创建

// // Created by 徐昌真 on 2024/10/5. // #include <iostream> using namespace std;int main() {//结构体的定义 struct 结构体名字 { 结构体成员名字 }struct Book{string name;double price;int value;}java; //java是创建的结构体//创建结构体//这是第一种方式Boo…

一篇文章吃透OA系统

一、OA系统是什么&#xff0c;都有什么功能&#xff1f; OA系统&#xff08;Office Automation System&#xff09;是办公自动化系统的简称&#xff0c;是一种利用计算机技术和网络通信技术&#xff0c;为企业和组织提供办公管理和协作支持的信息化系统。OA系统旨在提高办公效…

车载入行:HIL测试、功能安全测试、CAN一致性测试、UDS测试、ECU测试、OTA测试、TBOX测试、导航测试、车控测试

FOTA模块中OTA的知识点&#xff1a;1.测试过程中发现哪几类问题&#xff1f; 可能就是一个单键的ecu&#xff0c;比如升了一个门的ecu&#xff0c;他的升了之后就关不上&#xff0c;还有就是升级组合ecu的时候&#xff0c;c屏上不显示进度条。 2.在做ota测试的过程中&#xff…

Origin在数据表与图像之间切换

或者用快捷键 ALT 1 这儿可以切换