AQS理解

news2024/11/8 17:32:25

AQS是JAVA中的一组抽象类,就是为了解决多线程并发竞争共享资源而引发的线程安全问题,细致点说AQS就是具备一套线程阻塞等待以及被唤醒的时候锁分配的机制,这个机制是由队列来实现的,暂时获取不到所的线程加入到队列里面,AQS本身并没有实现太多的业务功能,只是对外提供了三点核心内容来帮助实现其他的并发内容

1)由int类型修饰的state属性

比如说Reentranlock和ReentrankReadWriteLock获取到的所的方式都是针对于state变量做修改来实现的,state等于1代表有线程获取到锁,CountDown计数器和Semphore计数器赋初值就是根据state来赋值的,state代表计数器;

2)由Node对象组成的双向链表,比如说ReentranLock有一个线程没有拿到锁资源,需要将线程封装成Node对象,并加Node对象加入到双向链表中,然后将线程挂起,进行阻塞等待;

3)由Node对象组成的单向链表,叫做ConditionObject,比如说ReentranLock,一个线程持有锁,执行了await()方法,此时这个线程就被封装成Node对象,并且被添加到单向链表里面

1)当new了一个ReenTranLock的时候,AQS默认就是head=tail=null,state等于0 

2)此时来了一个A线程,执行lock方法获取锁资源,CAS操作将state变成1,获取锁成功,A线程持有锁资源;

3)B尝试获取到锁资源,B线程尝试获取到锁资源,但是锁资源被A资源占用先创建一个Node节点作为傀儡节点也就是头节点,然后将当前这个失败的线程封装成一个Node,加入到这个傀儡节点的后面

4)挂起B线程,当前有一个ws属性,如果ws是-1,表示后面节点被挂起,等到A线程释放锁资源将state变成0然后去唤醒B线程,唤醒head.next

5)B线程就可以尝试重新获取到锁资源

ReenTranLock没有直接继承AQS,当执行到lock方法的时候发现执行了sync的lock方法,sync是一个抽象类,继承了AQS,Sync有两个子类实现一个是公平锁,一个是非公平锁

FairSync NonFairSync

//非公平锁的lock实现
final void lock() {
//线程到达以后直接尝试将state从0改成1,成功就拿到锁资源
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else//如果if中的CAS失败执行acquire方法直接走后续操作
                acquire(1);
        }
//公平锁的lock实现
final void lock() {
            acquire(1);
        }
一)acquire方法:
  public final void acquire(int arg) {
//1.查看tryAcquire方法,尝试再次去重新获取到锁,如果这个方法返回的是true,那么直接后面逻辑都不用走了
//2.查看addWaiter:没有获取到锁,要去排队了
//3.查看acquireQueued:挂起线程和后续被唤醒继续锁资源的逻辑
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
一)AQS的tryAcquire方法没有任何实现,必须被Reentranlock中的公平锁和非公平锁实现重写方法

非公平锁的实现:

1)没有线程持有锁,尝试获取锁

2)有线程获取锁,判断是不是可重入锁

3)如果上面两块都失败,直接返回false

 final boolean nonfairTryAcquire(int acquires) {
//1.获取到当前竞争锁失败的线程,获取到锁对象
            final Thread current = Thread.currentThread();
            int c = getState();
//2.然后再来进行判断公共状态state也就是c,然后再次尝试获取到锁
//state=0代表当前资源没有被锁住,此时当前线程可以尝试加锁占有资源
        if(c == 0) {
            if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);//将一个线程赋值给当前ReenTrankLock中的互斥锁拥有者的线程对象的属性
                    return true;
                }
            }
//3.此时获取到锁还是失败,锁某一个线程持有判断当前获取锁失败的线程是都等于当前之前获取锁成功的线程,就是判断是否是可重入锁,当前线程是独占线程那么每一次就+1
//state值不等于0 判断当前线程和持有当前资源线程是不是同一个线程,是,那就是可重入锁逻辑,就累加
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires=1;
                if (nextc < 0) // 防止加满int溢出
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

公平锁的实现:

1)hashQueedPredecessors()方法判断,判断自己是不是第一个

2)如果当前是队列的首元素,直接CAS尝试获取到锁compareAndSetState

 protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
//虽然锁资源没有被持有,但是需要判断有没有别的线程正在排队;
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
//直接将拥有锁的线程设置为自己
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }
 protected final void setExclusiveOwnerThread(Thread thread) {
        exclusiveOwnerThread = thread;
    }
二)addWaiter方法:将当前线程封装成Node对象,并且插入到AQS的双向链表

三)acquireQueued方法

private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        Node pred = tail;//当前队列尾节点是否存在
        if (pred != null) {//当前队列没有进行初始化
            node.prev = pred;//如果尾巴节点存在,直接插入到尾巴节点的后面
            if (compareAndSetTail(pred, node)) {
//CAS设置尾节点,CAS(tail,pred,node)
                pred.next = node;
                return node;
            }
        }
        enq(node);//创建队列
        return node;
    }

//这个代码是为了制定假设有若干个线程同时执行入队操作况且此时队列仍然是null
    private Node enq(final Node node) {
        for (;;) {//死循环确保这个对应的节点一定可以入队
            Node t = tail;
            if (t == null) { //如果尾巴节点等于null
                if (compareAndSetHead(new Node())
//需要注意的是,这里不是用的方法参数node,而是先创建了一个Node,并且head,tail都指向了这个空Node

//并发环境下设置头节点CAS(head,null,new Node())
//队列只是需要一个线程创建就可以了,后续的线程直接插入到队列的结尾即可
                    tail = head;//设置完成头节点,当前节点也是尾巴节点=null
            } else {
//执行入队操作,如果第一个线程创建队列成功,然后再走一次循环保证入队成功
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }
 AQS的工作流程:

1)线程请求同步资源:当一个线程请求某一个同步资源也就是尝试进行加锁的时候,AQS会尝试使用CAS来操作修改同步状态,如果成功获取到了锁,该线程可以继续执行

2)获取同步状态失败:如果说当前同步状态已经被其他线程占用了,锁被其他线程获取了,那么当前线程就需要将等待,AQS就会将该线程封装成一个节点,加入到双向链表中

3)自旋和阻塞:在等待队列中的线程会不断地进行自旋尝试获取到锁,如果自旋一定次数还是获取不到锁,那么就进入到阻塞状态,等待被唤醒

4)线程释放锁:当线程完成了对资源的操作需要释放锁的时候,这个线程就会调用AQS方法中的release方法,这个线程会使用CAS来修改同步状态,并唤醒等待队列中的一个线程或者是多个线程

5)等待唤醒线程:AQS在释放资源以后,会从队列中选择一个或者是多个线程并将其唤醒,被唤醒的线程会尝试再次去获取同步状态,如果获取成功,那么继续执行,如果获取失败,那么继续进入自旋或者是阻塞状态

 

 

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

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

相关文章

爬虫/scrapy基础

如果文章对你有帮助&#xff0c;欢迎关注、点赞、收藏一键三连支持以下哦&#xff01; 想要一起交流学习的小伙伴可以加zkaq222&#xff08;备注CSDN&#xff0c;不备注通不过哦&#xff09;进入学习&#xff0c;共同学习进步 目录 0x01 安装和简介 0x02 文件作用 0x04 保存…

【软考-中级】系统集成项目管理工程师-合同管理历年案例

持续更新。。。。。。。。。。。。。。。 目录 2018 下 试题一(17分)系列文章 2018 下 试题一(17分) 阅读下列说明&#xff0c;回答问题 1至问题 3&#xff0c;将解答填入答题纸的对应栏内     某大型央企 A 公司计划开展云数据中心建设项目&#xff0c;并将公司主要业务应…

OpenGL —— 2.8、漫游之摄像机飞行移动(附源码,glfw+glad)

源码效果 C源码 纹理图片 需下载stb_image.h这个解码图片的库&#xff0c;该库只有一个头文件。 具体代码&#xff1a; vertexShader.glsl #version 330 corelayout(location 0) in vec3 aPos; layout(location 1) in vec2 aUV;out vec2 outUV;uniform mat4 _modelMatrix; …

智慧公厕蜕变多功能城市智慧驿站公厕的创新

随着城市发展的不断推进&#xff0c;对公共设施的便利性和智能化要求也日益提高。为满足市民对高品质、便捷、舒适的公共厕所的需求&#xff0c;智慧公厕行业的领航厂家广州中期科技有限公司&#xff0c;全新推出了一体化智慧公厕驿站。凭借着“高科技碳中和物联网创意设计新经…

widnows安装audiocraft

audiocraft介绍 AudioCraft是一个PyTorch库&#xff0c;用于音频生成的深度学习研究。我们可以上传一段音乐&#xff0c;AI会根据音乐以及提示词&#xff0c;生成一段新的音乐。下面来看看具体安装。 首先查看cuda版本 如何安装cuda&#xff0c;可以自行搜索 获取程序 可…

泛微OA E-Office V10 OfficeServer 任意文件上传漏洞复现

声明 本文仅用于技术交流&#xff0c;请勿用于非法用途 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任。 漏洞简介 泛微e-ofice是一款标准化的协同0A办公软件&#xff0c;泛微 …

书单|1024程序员狂欢节充能书单!

作者简介&#xff1a; 辭七七&#xff0c;目前大二&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; 七七的闲谈 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f…

双指针——快乐数

一, 题目要求 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为&#xff1a; 对于一个正整数&#xff0c;每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个数变为 1&#xff0c;也可能是 无限循环 但始终变不到 1。 如果这个过程 结果为 …

工程中的SOVD——从ECU到车辆

SOVD标准正在改变诊断方式&#xff0c;特别是在互联网上当多个合作方进行交互时&#xff0c;其提供了很大的优势。在开发的早期阶段&#xff0c;需要使用附加的方法来利用这个标准&#xff0c;因为该标准并不是专为ECU诊断而开发的&#xff0c;而且还需格外注意数据的处理&…

金融统计学方法:神经网络

目录 1.神经网络 2.深度神经网络 3.案例分析 1.神经网络 神经网络是模仿人脑神经元工作原理而设计的一种算法模型。在一个基本的神经网络中&#xff0c;存在多个“神经元”或称为“节点”&#xff0c;这些节点被组织成多个层次。每个节点都接收前一层的输入&#xff0c;进行…

基于Python实现的一个通用的二进制数据分析工具源码,分析任意格式的二进制数据,还能同时查看协议文档

这是一个通用的二进制数据分析工具。 它能做什么 分析任意格式的二进制数据&#xff0c;还能同时查看协议文档逐字节、逐位分析手动、自动分析对分析结果建透视图&#xff0c;发现规律&#xff0c;学习协议 怎么做到的 工具以插件化方式扩展协议的支持定义了易用的API供插件…

【vue3】踩坑日记,vite与node版本对应(mac环境)

创建vue3vitets项目时&#xff0c;报错The requested module ‘vue’ does not provide an export named ‘computed’&#xff1b; node版本问题&#xff0c; Vite 需要 Node.js 版本 14.18&#xff0c;16&#xff1b; 升级node版本步骤&#xff1a; 先查看node的版本&#…

qt配置halcon环境实现读入图像显示

首先&#xff0c;在Halcon里面写流程。 *读入图像 read_image (Image, cat) *获取图片大小 get_image_size (Image, Width, Height) *设置适应大小显示 dev_set_part (0, 0, Height-1, Width-1) *显示图像 dev_display (Image) 菜单栏的文件->导出程序备用。 QT新建工程&a…

Jmeter测试关联接口

Jmeter用于接口测试时&#xff0c;后一个接口经常需要用到前一次接口返回的结果&#xff0c;本文主要介绍jmeter通过正则表达式提取器来实现接口关联的方式&#xff0c;可供参考。 一、实例场景&#xff1a; 有如下两个接口&#xff0c;通过正则表达式提取器&#xff0c;将第一…

小白学习c++的的第一节课

初识c 目录&#xff1a;一、c关键字(c98)二、命名空间2.1 命名空间的定义2.2 命名空间的使用 三、c输入与输出四、缺省参数五、函数重载六、引用6.1引用特性6.2常引用6.3使用场景6.4传值和传引用效率比较6.5引用和指针的区别 七、内联函数7.1 概念7.2特性 八、auto关键字&…

Mock 测试详解:什么是 Mock 测试

Mock测试 什么是 Mock &#xff1f; Mock 的意思就是&#xff0c;当你很难拿到源数据时&#xff0c;你可以使用某些手段&#xff0c;去获取到跟源数据相似的假数据&#xff0c;拿着这些假数据&#xff0c;前端可以先行开发&#xff0c;而不需要等待后端给了数据后再开发。 Mo…

通过尖端技术创造价值:释放生成式 AI 的力量

塔曼纳 一、说明 近年来&#xff0c;世界见证了人工智能&#xff08;AI&#xff09;的重大进步&#xff0c;生成式AI是最具革命性的技术之一。生成式人工智能已成为一种强大的方法&#xff0c;使机器能够创建新的原创内容&#xff0c;使其成为不同行业各种应用背后的驱动力。在…

An Embarrassingly Easy but Strong Baseline for Nested Named Entity Recognition

原文链接&#xff1a; https://aclanthology.org/2023.acl-short.123.pdf ACL 2023 介绍 问题 基于span来解决嵌套ner任务的范式&#xff0c;大多都是先对span进行枚举&#xff0c;然后对每个span进行分类&#xff0c;实际就是得到一个分数矩阵&#xff0c;矩阵中每个元素表示一…

uniapp map地图实现marker聚合点,并点击marker触发事件

1.uniapp官方文档说明 2.关键代码片段 // 仅调用初始化&#xff0c;才会触发 on.("markerClusterCreate", (e) > {})this._mapContext.initMarkerCluster({enableDefaultStyle: false, // 是否使用默认样式zoomOnClick: true, // 点击聚合的点&#xff0c;是否…

高级软件工程师职业发展路径及适学人群

大数据赋能产业&#xff0c;改变人与场景的交互方式&#xff0c;随着大数据在各行各业融合应用的不断深化&#xff0c;预计2020年中国大数据市场市值将突破10000亿元。 未来发展&#xff0c;大数据应用范围越加宽广&#xff0c;包括&#xff1a;新零售、旅游、广告、电商…