首页 > 编程语言 >AQS源码详解

AQS源码详解

时间:2023-06-06 16:22:22浏览次数:32  
标签:node Node 结点 AQS 队列 源码 线程 方法 详解

AQS源码详解

可重入锁:同一个线程可重复获取同一把锁对象

locksupport:用来创建锁和其他同步类的基本线程阻塞原语 park()和unpark()

为什么会引出locksupport?
像传统的synchorized和lock,他们的wait()和notify()方法,await()和singal()方法使用不方便,必须在同步代码块或者锁内使用,并且先后顺序不能调用,否则会报错和出现死锁情况。

park和unpark方法

park 就是消耗当前调用该方法的线程的凭证,凭证最少为0,如果凭证为0则会进行线程的阻塞,等待凭证可用
unpark就是增加当前调用该方法的线程的凭证,凭证最多为1

锁: 面向锁的使用者
同步器:面向锁的实现者

AQS:抽象队列同步器
将阻塞等待的线程用Node结点进行封装到一个双向队列中,每个node结点都记录了waitStatus,并且整个AQS记录了一个state的状态位。

这里我们使用非公平锁进行代码的追踪

image-20230320173410651

利用CAS先进行比较,我们期待的值为0,希望更新的值为1,如果status为0说明没人使用这把锁,如果status为1说明已经有人使用这把锁,那么cas将会失败,则会走acquire(1)方法

image-20230320174026813

第一个tryAcquire(arg)方法

image-20230320200952396

子类必须实现此模板方法,否则会抛出异常,定义规范,钩子函数

实现类如下:

image-20230320201100178

这里我们是使用非公平锁,所以选择第二个实现类,点进去之后如下:

image-20230320201257323

image-20230320201326211

​ 大概逻辑就是先获取aqs中的state的值,如果为0说明锁没有被占用,该线程可以获取,如果不为0说明锁已经被其他线程占用了,但是有可能是持有该锁的线程重新获取这把锁,所以就是可重入锁的判断,即else-if内部代码逻辑。

​ 所以由于之前添加了线程a拿到了锁,所以线程b无法获取,那么tryAcquire(arg)方法返回false,那么!tryAcquire(arg)就是true,就会执行后面判断中的acquireQueued(addWaiter(Node.EXCLUSIVE),arg))方法,然后就是看addWaiter(Node.EXCLUSIVE)方法:EXCLUSIVE代表独占式的。

image-20230320203515722

​ 大概逻辑就是将线程封装成结点入队的操作

​ Node结点最重要的几个属性就是waitStauts,prev,next,head,tail,thread属性

​ 由于前面线程中还没有进入过队列,所以一开始tail为空,if语句就不成立了,会进入enq(node)方法:

image-20230320203955709

​ 前面说了一开始队列为空时,tail为空,会进入到if(t==null)语句内部对队列进行初始化操作,就是创建一个空结点作为哨兵结点,让头节点的tail和head指向该空结点。然后for是死循环,此时if语句是不成立,会进入到else语句,那么就会将前面封装好的线程B结点插入到哨兵结点的后面返回。

​ 后面的C,D线程获取锁就不会走enq(node)方法了,因为此时队列里面有两个结点分别为哨兵结点和线程B结点,所以addWaiter(mode)方法中的tail是不为空的,会进入到if语句代码块中,会将C,D线程结点分别插入到队列中去返回。

image-20230320211926960

​ addWaiter的返回值就作为acquireQueued方法中node的实参值,node为新添加线程结点的值,node.predecessor()方法是获取node结点的前一个结点的值,因为线程b结点前一个是哨兵结点,所以这里p是一个哨兵结点,所以p==head成立,然后还有一个tryAcquire(arg)方法的判断,这里当然获取锁失败,因为锁已经被线程a占用了,所以整个if语句返回false。然后会来到shouldParkAfterFailedAcquire(p, node)方法的判断:

image-20230320212420617

这里的判断逻辑就是看看node前一个结点的waitStatus的值是否为Node.SIGNAL==-1或者大于0,如果都不是则会执行compareAndSetWaitStatus

方法,设为Node.SIGNAL==-1,最后shouldParkAfterFailedAcquire方法返回false,不会执行后面的parkAndCheckInterrupt方法,然后自旋后,再次进入到shouldParkAfterFailedAcquire方法后ws为-1最后返回true。所以说该方法执行了两次。

返回true之后就会执行if判断语句后面的parkAndCheckInterrupt()方法,这里就会用到我们前面学到的LockSupport.park(object)线程阻塞方法,注意此时线程b就会被阻塞等待唤醒,即线程b会一直卡在parkAndCheckInterrupt()方法中等待着unpark()将其唤醒。此时线程b才是真真正正被阻塞在队列里面。

 if(shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()){}

image-20230320213127026

标签:node,Node,结点,AQS,队列,源码,线程,方法,详解
From: https://www.cnblogs.com/sunshineTv/p/17460842.html

相关文章

  • Elasticjob 3.x 最新版本源码解读
    源码地址(含备注):https://gitee.com/ityml/elastic-job-zgc官方网站:https://shardingsphere.apache.org/elasticjob/ElasticJob是面向互联网生态和海量任务的分布式调度解决方案,由两个相互独立的子项目ElasticJob-Lite和ElasticJob-Cloud组成。它通过弹性调度、资源管......
  • python切片详解
    索引的一般方式一个完整的切片是包含三个参数和两个冒号":",用于分隔三个参数(start_index、end_index、step)。当只有一个“:”时,默认第三个参数step=1;当一个“:”也没有时,start_index=end_index,表示切取start_index指定的那个元素。切片操作的基本表达式:object[start......
  • 直播平台搭建源码,调用系统相册实现多选图片上传
    直播平台搭建源码,调用系统相册实现多选图片上传1、首先需要给webview的WebChromeClient设置以下代码,才可以实现h5与Android交互选取图片 privateValueCallback<Uri>mValueCallbackUri;privateValueCallback<Uri[]>mValueCallbackUris;this.setWebChromeClient(newWebChro......
  • 统信UOS系统开发笔记(三):从Qt源码编译安装之编译安装Qt5.12.8
    前言  上一篇,是使用Qt提供的安装包安装的,有些场景需要使用到自己编译的Qt,所以本篇如何在统信UOS系统上编译Qt5.12.8源码。<br>统信UOS系统版本  系统版本:  Qt源码下载  参考博文《获取下载Qt安装包,Qt源码全国网址备忘录(不用注册Qt账户,即可下载各版本Qt安装包和Qt源......
  • 考古笔记14:访问控制列表ACL详解(真的很详细)
    访问控制列表 ACL(AccessControlList,访问控制列表)        技术从来都是一把双刃剑,网络应用与互联网的普及在大幅提高企业的生产经营效率的同时,也带来了诸如数据的安全性,员工利用互联网做与工作不相干事等负面影响。如何将一个网络有效的管理起来,尽可能的降低网络所带......
  • xades4j 苦苦寻找的是啥 (源码 == 找到了测试用例 == 找到了用法)
    <dependency><groupId>com.googlecode.xades4j</groupId><artifactId>xades4j</artifactId><version>1.3.2</version></dependency>https://github.com/luisgoncalves/xades4j源码和junit(大量的测试用例,告诉我们什么是xades......
  • Storm-源码分析-Topology Submit-Client
    1StormClient最开始使用storm命令来启动topology,如下stormjarstorm-starter-0.0.1-SNAPSHOT-standalone.jarstorm.starter.WordCountTopology这个storm命令是用python实现的,看看其中的jar函数,很简单,调用exec_storm_class,其中jvmtype=”-client” 而exec_storm_clas......
  • Qt迭代器(Java类型和STL类型)详解
    迭代器为访问容器类里的数据项提供了统一的方法,Qt有两种迭代器类:Java类型的迭代器和STL类型的迭代器。两者比较,Java类型的迭代器更易于使用,且提供一些高级功能,而STL类型的迭代器效率更高。 Java类型迭代器对于每个容器类,有两个Java类型迭代器:一个用于只读操作,一个用于......
  • Qt多窗口编程详解
    常用的窗体基类是QWidget、QDialog和QMainWindow,在创建GUI应用程序时选择窗体基类就是从这3个类中选择。QWidget直接继承于QObject,是QDialog和QMainWindow的父类,其他继承于QWidget的窗体类还有QSplashScreen、QMdiSubWindow和QDesktopWidget。另外还有一个类Q......
  • iTOP-3588开发板Android12源码定制开发uboot开发
    uboot开发-Uboot源码是v2017.09版本。目前在该平台上已经支持RK所有主流在售芯片。支持的功能主要有:支持RKAndroid固件启动;支持AndroidAOSP固件启动;支持LinuxDistro固件启动;支持Rockchipminiloader和SPL/TPL两种Pre-loader引导;支持LVDS、EDP、MIP......