- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章彻底了解java中ReentrantLock和AQS的源码由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
首先在聊ReentrantLock之前,我们需要知道整个JUC的并发同步的基石,currrent里面所有的共享变量都是由volatile修饰的,我们知道volatile的语义有2大特点,可见性以及防止重排序(内存屏障,volatie写与volatile读) 1、当第二个操作为volatile写操做时,不管第一个操作是什么(普通读写或者volatile读写),都不能进行重排序。这个规则确保volatile写之前的所有操作都不会被重排序到volatile之后,
2、当第一个操作为volatile读操作时,不管第二个操作是什么,都不能进行重排序。这个规则确保volatile读之后的所有操作都不会被重排序到volatile之前,
3、当第一个操作是volatile写操作时,第二个操作是volatile读操作,不能进行重排序。 而cas操作同时包含了volatile写/读语义,这二者的完美结合就组成了current的基石 。
1. 。
运行结果 。
reentrantlock用于替代synchronized 。
2.ReentrantLock还有一个tryLock(time),可以指定时间,如果指定时间内没有获得锁,则放弃,可以通过其返回值来决定是否继续等待 。
3.还有就是Condition了(我个人觉得这是最灵活的一个地方) 。
这是condition结合lock(独占锁)的用法 condition目前只实现了独占锁,关于condition的源码理解,后续也会继续更新,暂时我们只需要知道类似object的wait与notifiy 。
我们现在知道了基本用法,那么我们就可以开始探究源码了 。
1.AQS 我们知道JUC里面的核心类就是AQS,那么AQS究竟是个啥东西呢? 1)先上内部类NODE源码 。
不知道大家在看到这个 Node next;Node prev;的时候是啥感觉,反正我当时是激动坏了,这不就是一个双向链表嘛, 再看volatile Thread thread;这个属性,这是一个管理线程的双向链表,换句话说就是将线程打包成立节点放入AQS的链表中 基础的结构清楚之后。 SHARED 与EXCLUSIVE 代表是独占节点还是共享节点 2)再上AQS属性源码 。
Node0 head与tail不用说,这是来管理节点的, 这里我们要核心介绍一个属性是state,这也是AQS这个类的灵魂, 1.再独占锁中这个state是1或者0,(如果大于1则表示锁重入,这个稍后会有源码分析) 2在共享锁中代表还有多少共享锁资源, 3.在读写锁中,高16位代表写锁是否被占用,低16位代表有多少读锁, 4.在CountDownLatch中,通过构造参数代码门闸剩余个数 5.在Semaphore中,同样通过构造参数代表信号灯个数 。
2.ReentrantLock获取锁源码(独占锁) 首先公平锁与非公平锁,分别继承与Sync FairSync NoFairSync ,默认是非公平锁,可以在构造方法上指定 。
公平锁故名思意,在AQS中管理着一个线程队列,如果这时候 有一个线程过来抢这把锁,如果是公平锁,那么会判断队列是不是存在不同与当前线程的等待队列(FIFO),如果存在则去排队,非公平锁则是直接去排队 (2.1.非公平锁获取锁) 。
首先cas原子操作尝试去修改值,如果修改成功说明成功获取到了锁,进入setExclusiveOwnerThread()方法 。
将当前线程记录,实现偏向锁,一行代码便完美实现了偏向锁!! 。
如果失败则,调用acquire();这个方法调用的实际上是子类的nonfairTryAcquire方法 。
首先,获取state的值,判断是否为0,如果为0,则说明锁没有被占有,(可能是刚刚被释放)那么cas操作开始尝试获取锁, **(注意注意注意)**重要的事情说三遍,这里仅仅尝试获取一次,没有自旋!!这是独占锁与共享锁的区别之一,因为如果state>=0(对于共享锁来说state代表剩余数量),那么共享锁会不断尝试自旋获取锁,之道state<0,因为只要》0那么就可能共享到锁 接下来的else if就是重入锁的操作了,判断当先线程是不是记录的线程,如果是,每次重入state+1,如果不是就返回flase,直接拜拜 (2.2公平锁获取锁) 刚刚介绍了公平锁的意义,所有直接上源码,公平锁比非公平锁多了一个公平判断 。
我们可以看到hasQueuedPredecessors是用来判断队列是否有不同于当前线程的节点等待,这里重点讨论一个情况, h != t返回true,(s = h.next) == null返回true 首先可以知道队列中有2个节点,但是头节点没有后继结点,在这里列举一种情况,有另一个线程已经执行到初始化队列的操作了,介于compareAndSetHead(new Node())与tail = head之间,也就是之后说的enq自旋方法,请继续往下看 。
继续如果非公平锁没有获取到锁,那么会调用acquireQueued和addwaiter方法 。
首先来看addwaiter方法 。
首先将,当前线程封装成一个,Node,然后通过判断尾结点是不是空的方式,判断队列是不是空的,如果存在尾结点,那么直接进先驱后继的改造,放入双向链表,完成链表结构,那么如果是空的呢?这么调用了一个enq的自旋方法 。
我们可以看到这个一个自旋方法,第一次循环:t为null,那么cas就new出来一个新结点,头尾都指向这个新结点, 第二次循环,将传进来的这个线程结点的前驱指向刚刚new的这个结点,然后cas操作进行,将这个线程结点替换为尾部结点,然后head后继指向线程结点,返回head 经过二次循环,得到了一个由2个节点组成的队列,head-》node,head是假节点(里面不包括线程为null),node才是真正的线程结点(addwrite封装好的传进来的线程结点) 问题1.为什么一定要用cas操作,因为防止别的线程修改了该队列 。
好,现在我们继续,看acquireQueued 。
这是一个最最核心的方法,从队列中取线程 首先这是一个自旋,判断该节点的前驱节点是不是head,因为(FIFO)先进先出队列。 如果不是直接拜拜进入shouldParkAfterFailedAcquire,继续上源码 。
static final int CANCELLED = 1; static final int SIGNAL = -1; static final int CONDITION = -2; static final int PROPAGATE = -3,
首先说明一下waitStatus这个属性,为什么之前不提呢,因为之前没有对waitStatus进行操作,我们在new节点与封装结点的时候没有考虑这个属性,所以我们现在当成一个新属性,值为0来看待, 。
shouldParkAfterFailedAcquire这个代码的逻辑意义是说明呢? 如果是SIGNAL那么,直接ture, 如果大于0,则是CANCELLED,被取消了,直接剔除队列 如果都不是,那么将其前驱结点设为SIGNAL,也就是可以安心睡觉了,定好闹钟了,可以被等着唤醒了, 我们现在很明显是第三种,因为之前啥都没干,就是0, 所以本来状态 。
进行shouldParkAfterFailedAcquire之后, 那么现在链表中,对线程1的前驱设闹钟,0变成-1 。
假设这时候又来了个线程2,那么同理,对线程而的先驱设闹钟0变成-1 。
在调用了shouldParkAfterFailedAcquire()之后,调用parkAndCheckInterrupt方法用于阻塞, 。
这里提一下,关于parkAndCheckInterrupt,lock里面用于阻塞都是基于lockSupper.park()与lockSupper.unpark()完成了,而lockSupper调用的又是unsafe这个类,我们知道java是基于jvm实现的,并不能和c++一样直接对os进行操作,所以jvm给我们提供了一个梯子,这个梯子就是unsafe这个类,直接将线程的的交个操作系统阻塞 。
终于到了释放锁,独占锁的释放锁的逻辑相对与共享锁来说比较简单,后续我也会继续更新共享锁的源码 。
首先我们来看tryRelease方法, 。
首先获取,int c = getState() - releases;这里可能c是>0的,因为独占锁的重入锁(上面以及说明了独占锁的重入的源码操作),所以有可能是需要进行多次解锁的,继续 判断当前线程是不是独占线程,如果不是则报IllegalMonitorStateException异常 一直解锁到c=0的时候,那么线程已经解锁,则设setExclusiveOwnerThread=null 设置当前独占线程为null,然后设置state为0 。
继续回到release,如果头节点不是null而且h.waitStatus != 0,说明是-1,说明设置闹钟了,需要唤醒aqs队列中的阻塞结点,调用的是unparkSuccessor方法,继续看源码 。
这里首先明确,这里传进来的node是啥?是头节点!!!!,一定要明确这个,博主就是因为刚开始没明确这个,半天没明白,因为唤醒一定是唤醒头节点之后的waitStatus不为1的结点 。
首先判断头节点,如果是-1则设置为0,这个中间状态,表示有结点被唤醒了, 然后拿到,head的后继节点,进行判断,如何是null或者waitStatus >0,(就是1,CANCELLED,代表被取消了),这个时候从尾部开始遍历,剔除waitStatus >0的节点,找到第一个waitStatus <=0的节点,用LockSupport.unpark(s.thread);将其唤醒,唤醒之后的线程回到acquireQueued方法中, 。
首先,将该结点设置为head,然后将老head指向null,帮助gc回收,然后return返回,至此线程自由 那么此时该队列为 。
写了很久,如果有啥不对的地方,欢迎大家指正 。
以上就是一篇彻底看懂ReentrantLock,AQS的源码的详细内容,更多关于一篇彻底看懂ReentrantLock,AQS的源码的资料请关注我其它相关文章! 。
原文链接:https://blog.csdn.net/weixin_44213940/article/details/115432633 。
最后此篇关于彻底了解java中ReentrantLock和AQS的源码的文章就讲到这里了,如果你想了解更多关于彻底了解java中ReentrantLock和AQS的源码的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我开始在 Ethereum blockchain 上了解如何开发智能合约以及如何写 web-script用于与智能合约交互(购买、销售、统计......)我得出了该怎么做的结论。我想知道我是否正确理解
我正在 UIView 中使用 CATransform3DMakeRotation,并且我正在尝试进行 45º,变换就像向后放置一样: 这是我拥有的“代码”,但显然没有这样做。 CATransform3
我目前正在测试 WebRTC 的功能,但我有一些脑逻辑问题。 WebRTC 究竟是什么? 我只读了“STUN”、“P2P”和其他...但是在技术方面什么是正确的 WebRTC(见下一个) 我需要什么
我在看 DelayedInit在 Scala in Depth ... 注释是我对代码的理解。 下面的 trait 接受一个非严格计算的参数(由于 => ),并返回 Unit .它的行为类似于构造函数
谁能给我指出一个用图片和简单的代码片段解释 WCF 的资源。我厌倦了谷歌搜索并在所有搜索结果中找到相同的“ABC”文章。 最佳答案 WCF 是一项非常复杂的技术,在我看来,它的文档记录非常少。启动和运
我期待以下 GetArgs.hs打印出传递给它的参数。 import System.Environment main = do args main 3 4 3 :39:1: Coul
private int vbo; private int ibo; vbo = glGenBuffers(); ibo = glGenBuffers(); glBindBuffer(GL_ARRAY_
我正在尝试一个 for 循环。我添加了一个 if 语句以在循环达到 30 时停止循环。 我见过i <= 10将运行 11 次,因为循环在达到 10 次时仍会运行。 如果有设置 i 的 if 语句,为什
我正在尝试了解 WSGI 的功能并需要一些帮助。 到目前为止,我知道它是一种服务器和应用程序之间的中间件,用于将不同的应用程序框架(位于服务器端)与应用程序连接,前提是相关框架具有 WSGI 适配器。
我是 Javascript 的新手,我正在尝试绕过 while 循环。我了解它们的目的,我想我了解它们的工作原理,但我在使用它们时遇到了麻烦。 我希望 while 值自身重复,直到两个随机数相互匹配。
我刚刚偶然发现Fabric并且文档并没有真正说明它是如何工作的。 我有根据的猜测是您需要在客户端和服务器端都安装它。 Python 代码存储在客户端,并在命令运行时通过 Fabric 的有线协议(pr
我想了解 ConditionalWeakTable .和有什么区别 class ClassA { static readonly ConditionalWeakTable OtherClass
关闭。这个问题需要更多focused .它目前不接受答案。 想改善这个问题吗?更新问题,使其仅关注一个问题 editing this post . 5年前关闭。 Improve this questi
我还没有成功找到任何可以引导我理解 UIPickerView 和 UIPickerView 模型的好例子。有什么建议吗? 最佳答案 为什么不使用默认的 Apple 文档示例?这是来自苹果文档的名为 U
我在看foldM为了获得关于如何使用它的直觉。 foldM :: Monad m => (a -> b -> m a) -> a -> [b] -> m a 在这个简单的例子中,我只返回 [Just
答案What are _mm_prefetch() locality hints?详细说明提示的含义。 我的问题是:我想要哪一个? 我正在处理一个被重复调用数十亿次的函数,其中包含一些 int 参数。
我一直在读这个article了解 gcroot 模板。我明白 gcroot provides handles into the garbage collected heap 然后 the handle
提供了一个用例: 流处理架构;事件进入 Kafka,然后由带有 MongoDB 接收器的作业进行处理。 数据库名称:myWebsite集合:用户 并且作业接收 users 集合中的 user 记录。
你好 我想更详细地了解 NFS 文件系统。我偶然发现了《NFS 图解》这本书,不幸的是它只能作为谷歌图书提供,所以有些页面丢失了。有人可能有另一个很好的资源,这将是在较低级别上了解 NFS 的良好开始
我无法理解这个问题,哪个更随机? rand() 或: rand() * rand() 我发现这是一个真正的脑筋急转弯,你能帮我吗? 编辑: 凭直觉,我知道数学答案是它们同样随机,但我忍不住认为,如果您
我是一名优秀的程序员,十分优秀!