Reentrantjava readwritelockk类和ReentrantLock类的区别

Java concurrency之共享锁和ReentrantReadWriteLock_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
Java concurrency之共享锁和ReentrantReadWriteLock
|0|0|文档简介
北京动力节点教育科技有限公司,自2009成立...|
总评分0.0|
&&Java concurrency之共享锁和ReentrantReadWriteLock
阅读已结束,如果下载本文需要使用0下载券
想免费下载更多文档?
定制HR最喜欢的简历
下载文档到电脑,查找使用更方便
还剩12页未读,继续阅读
定制HR最喜欢的简历
你可能喜欢博客频道 - CSDN.NET
前言(1)本文共花费2周零3天的凌晨时光,这段时间收获很多.(2)从整理文章,作者从线程--&阻塞队列--&二进制--&线程池的内部机制,一路走来,本来是想写一篇为AsyncTask做铺垫的文章,没想...
1小时前289
你要知道的runtime都在这里转载请注明出处 /p/e2c0c67d39ed本文主要讲解runtime相关知识,从原理到实践,由于包含内容过多分为以下五篇...
2小时前138
如何减小APK的体积?
如果你的app看起来很大,用户通常避免下载,特别是在很多新兴的国家,人们使用2G和3G网络接入因特网,和按流量计费。这篇文章介绍如何减小app的体积,这会帮助更多的用户来下载你...
2小时前138
上一篇使用caffenet的模型微调,但因为caffenet有220M太大,测试速度太慢,因此换为googlenet.
迭代了2800次时死机,大概20分钟。使用的是2000次的模型。
2小时前138
推荐算法是否是影响广告系统价值的唯一因素?是否还有其他因素在影响广告系统的价值?甚至这些因素比推荐算法更加重要?
3小时前143
“蓟门桥西到了。请乘客有序上下车,下车请刷卡。”  随着公交车上报音员的声音响起,公交车缓缓的停了下来。  林萧从公交车上下来,便直奔宿舍而去。方才在培训机构,林萧第一个离开了教室,这倒不是他自恃项目...
3小时前367
JAVA并行程序基础
在面向线程设计的计算机结构中,进程是线程的容器。我们都知道,程序是对于指令、数据及其组织形式的描述,而进程是程序的实体。
线程是轻量级的进程,是程序执行的最小单位。(PS:使用多...
3小时前167
前文列出的代码给大家展示了一个最简单的网络程序,但正如文章末尾所提的,这个最简单的网络程序最大的缺点是服务端一次只能服务一个客户端,就比如说你去吃饭,饭店只有一个服务员, 而且服务员在客户离开之前只能...
3小时前164
Android弹幕实现:基于B站弹幕开源系统(2)在附录1的基础上,模拟实现一种实际开发的应用场景:从网络中不间断的周期取弹幕数据,这些弹幕数据往往是批量的,然后把这些从网络中取到的批量数据逐个的显示...
3小时前157
基于kafka的生产者消费者消息处理
3小时前156
github/sea-boat/net-reactornet-reactorit’s a simple and easy net framework with ni...
3小时前260
xUtils3源码解析之图片模块,图片加载所需要的一切,都在这里~
3小时前187
4小时前160
该文主要讲述基本排序算法:冒泡排序,选择排序,插入排序;高级排序算法:希尔排序,归并排序,快速排序的算法实现(JavaScript)和相同数据下的运行时间对比。
4小时前156
SSH框架的基本整合
5小时前225
你要知道的runtime都在这里转载请注明出处 /p/0623addb6b74本文主要讲解runtime相关知识,从原理到实践,由于包含内容过多分为以下五篇...
6小时前214
Form个性化开发内容不是很多,在国内的项目上客户化界面上基本用不上,相关开发文档有的讲解的很详细,有的讲解太过简练,希望通过这个文档可以给我们这些刚开始做个性化的新手快速完成需求,不要再这方面花费过...
13小时前332
朴素贝叶斯模型简述:贝叶斯模型通过使用后验概率和类的概率分布来估计先验概率,具体的以公式表达为P(Y)可以使用训练样本的类分布进行估计。如果X是单特征也很好估计,但如果X={x1,x2,..,xn}等...
13小时前399
静态原型的天气预报未来5天的天气预报,可以看成由5个类似的天气模块构成,只要完成一个,其他几个就可以通过复制+修改内容实现改变。首先来明确一下各个组件的尺寸和参数,
整个区域的大小,我设定成360dp...
昨天 22:16556
推送是在日常终端使用场景中经常碰到,特别是移动互联网普及之后,手机终端成为了消息推送的主战场,例如生活服务类的优惠券推送,咨询类的新闻推送,电商类的购物推送等等,在业务用户触达上起到了至关重要的作用,...
昨天 21:24555
加载更多...
Programmer
研究方向视觉slam,移动机器人自主导航
作为程序员的你,会选择奋斗在一线城市还是回归故乡发展?
奋斗在一线城市72%
回归故乡发展28%
专家公开课
讲师:郑宜东
专家图书推荐
5年多Java开发经验,1年多管理经验,目前任职于阿里中间件团队,专注云计算方面的研发。
现任腾讯高级工程师(T3-2),QQ会员体系合作线技术团队负责人,负责生活特权和AMS平台...
中科院自动化研究所博士,研究方向:计算机视觉,机器学习。
推荐知识库
热文排行榜
联系客服:
CSDN博客QQ群号:说到ReentrantReadWriteLock,首先要做的是与ReentrantLock划清界限。它和后者都是单独的实现,彼此之间没有继承或实现的关系。 然后就是总结这个锁机制的特性了:
(a).重入方面其内部的WriteLock可以获取ReadLock,但是反过来ReadLock想要获得WriteLock则永远都不……
声明:该文章系网友上传分享,此内容仅代表网友个人经验或观点,不代表本网站立场和观点;若未进行原创声明,则表明该文章系转载自互联网;若该文章内容涉嫌侵权,请及时向
论文写作技巧
上一篇:下一篇:
相关经验教程& java并发锁ReentrantReadWriteLock读写锁源码分析
java并发锁ReentrantReadWriteLock读写锁源码分析
1、ReentrantReadWriterLock基础
所谓读写锁,是对访问资源共享锁和排斥锁,一般的重入性语义为 如果对资源加了写锁,其他线程无法再获得写锁与读锁,但是持有写锁的线程,可以对资源加读锁(锁降级);如果一个线程对资源加了读锁,其他线程可以继续加读锁。
java.util.concurrent.locks中关于多写锁的接口:ReadWriteLock
提一个问题,是否觉得ReentrantReadWriteLock会实现Lock接口吗?与ReentrantLock有什么关系?
答案是否定的,ReentrantReadWriterLock通过两个内部类实现Lock接口,分别是ReadLock,WriterLock类。与ReentrantLock一样,ReentrantReadWriterLock同样使用自己的内部类Sync(继承AbstractQueuedSynchronizer)实现CLH算法。为了方便对读写锁获取机制的了解,先介绍一下Sync内部类中几个属性,采用了位运算:
首先ReentrantReadWriterLock使用一个32位的int类型来表示锁被占用的线程数(ReentrantLock中的state),用所以,采取的办法是,高16位用来表示读锁占有的线程数量,用低16位表示写锁被同一个线程申请的次数。
SHARED_SHIFT,表示读锁占用的位数,常量16
SHARED_UNIT,
增加一个读锁,按照上述设计,就相当于增加 SHARED_UNIT;
,表示申请读锁最大的线程数量,为65535
EXCLUSIVE_MASK
:表示计算写锁的具体值时,该值为 15个1,用 getState & EXCLUSIVE_MASK算出写锁的
线程数,大于1表示重入。
static int sharedCount(int c)
{ return c &&& SHARED_SHIFT; }
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
举例说明,比如,现在当前,申请读锁的线程数为13个,写锁一个,那state怎么表示?
上文说过,用一个32位的int类型的高16位表示读锁线程数,13的二进制为 1101,那state的二进制表示为
00 ,十进制数为851969, 接下在具体获取锁时,需要根据这个851968这个值得出上文中的 13 与 1。要算成13,只需要将state 无符号向左移位16位置,得出01101,就出13,根据851969要算成低16位置,只需要用该00
& 111(15位),就可以得出,就是利用了1&1得1,1&0得0这个技巧。
移位元素,如果一个数值向左移(&)一位,在没越界(超过该类型表示的最大值)的情况下,想当于操作数 * 2
如果一个数值向右(&) 移动移位,在没有越界的情况下,想到于操作数 除以2。
然后再关注如下几个与线程本地变量相关的属性:
上述这4个变量,其实就是完成一件事情,将获取读锁的线程放入线程本地变量(ThreadLocal),方便从整个上 下文,根据当前线程获取持有锁的次数信息。其实 firstReader,firstReaderHoldCount ,cachedHoldCounter 这三个变量就是为readHolds变量服务的,是一个优化手段,尽量减少直接使用readHolds.get方法的次数,firstReader与firstReadHoldCount保存第一个获取读锁的线程,也就是readHolds中并不会保存第一个获取读锁的线程;cachedHoldCounter 缓存的是最后一个获取线程的HolderCount信息,该变量主要是在如果当前线程多次获取读锁时,减少从readHolds中获取HoldCounter的次数。请结合如下代码理解上述观点:
2、ReentrantReadWriterLock源码分析
2.1 ReadLock 源码分析
2.1.1 lock方法
sync.acquireShared方法存在于AbstractQueuedSynchronizer类中,
根据常识,具体获取锁的过程在子类中实现,果不其然,tryAcquireShared方法在ReentrantReadWriterLock的Sync类中实现
尝试获取共享锁代码解读:
@1 start--end ,如果有线程已经抢占了写锁,并且不是当前线程,则直接返回-1,通过排队获取锁。
@2,如果线程不需要阻塞,并且获取读锁的线程数没有超过最大值,并且使用 CAS更新共享锁线程数量成功的话;表示成获取读锁,然后进行内部变量的相关更新操作;先关注一下,成功获取读锁后,内部变量的更新操作:
@21,如果r=0, 表示,当前线程为第一个获取读锁的线程
@22,如果第一个获取读锁的对象为当前对象,将firstReaderHoldCount 加一
@23,成功获取锁后,如果不是第一个获取多锁的线程,将该线程持有锁的次数信息,放入线程本地变量中,方便在整个请求上下文(请求锁、释放锁等过程中)使用持有锁次数信息。
@3 在讲解代码@3之前,我们先重点分析@2处的第一个条件,是否需要阻塞方法:readerShouldBlock,在具体的子类中,现在查看的是NonfairSync中的方法:
该方法如果头节点不为空,并头节点的下一个节点不为空,并且不是共享模式【独占模式,写锁】、并且线程不为空。则返回true,说明有当前申请读锁的线程占有写锁,并有其他写锁在申请。为什么要判断head节点的下一个节点不为空,或是thread不为空呢?因为第一个节点head节点是当前持有写锁的线程,也就是当前申请读锁的线程,这里,也就是锁降级的关键所在,如果占有的写锁不是当前线程,那线程申请读锁会直接失败。
现在继续回到@3,讲解如果第一次尝试获取读锁失败后,该如何处理。首先,进入该方法的条件如下:
1),没有写锁被占用时,尝试通过一次CAS去获取锁时,更新失败(说明有其他读锁在申请)。
2),当前线程占有写锁,并且没有有其他写锁在当前线程的下一个节点等待获取写锁。;其实如果是这种情况,除非当前线程占有锁的下个线程取消,否则进入fullTryAcquireShared方法也无法获取锁。
代码@31,首先再次判断,如果当前线程不是写锁的持有者,直接返回-1,结束尝试获取读锁,需要排队去申请读锁。
代码@32,如果需要阻塞,说明除了当前线程持有写锁外,还有其他线程已经排队在申请写锁,故,即使申请读锁的线程已经持有写锁(写锁内部再次申请读锁,俗称锁降级)还是会失败,因为有其他线程也在申请写锁,此时,只能结束本次申请读锁的请求,转而去排队,否则,将造成死锁。代码@34,就是从readHolds中移除当前线程的持有数,然后返回-1,结束尝试获取锁步骤(结束tryAcquireShared 方法)然后去排队获取。
代码@33,因为,如果当前线程是第一个获取了写锁,那其他线程无法申请写锁(该部分在分析完,读写锁的队列机制后,才回来做更详细的解答。)
代码@35,表示成功获取读锁,后续就是更新readHolds等内部变量,该部分在上文中已有讲解。如果是通过@35尝试获取锁成功,这就是写锁内部--》再次申请读锁(锁降级)的原理。
至此,完成尝试获取锁步骤 tryAcquireShared 方法,我们再次回到 acquireShared,如果返回-1,那么需要排队申请,具体请看 doAcquireShared(arg);
获取共享锁解读:
代码@1,在队列尾部增加一个节点。锁模式为共享模式。
代码@3,获取该节点的前置节点。
代码@4,如果该节点的前置节点为head(头部),为什么前置节点是head时,可以再次尝试呢?在讲解ReentrantLock时,也讲过,head节点的初始化在第一次产生锁争用时初始化,刚开始初始化的head节点是不代表线程的,故可以尝试获取锁。如果获取失败,则将进入到shouldParkAfterFailedAcquire和parkAndCheckInterrupt方法中,线程阻塞,等待被唤醒。
重点分析一下获取锁后的操作:setHeadAndPropagate
释放共享锁的步骤:
代码@1,如果读锁(共享锁)获取成功,或头部节点为空,或头节点取消,或刚获取读锁的线程的下一个节点为空,或在节点的下个节点也在申请读锁,则在CLH队列中传播下去唤醒线程,怎么理解这个传播呢,就是只要获取成功到读锁,那就要传播到下一个节点(如果一下个节点继续是读锁的申请,只要成功获取,就再下一个节点,直到队列尾部或为写锁的申请,停止传播)。具体请看doReleaseShared方法。
代码@4,从队列的头部开始遍历每一个节点。
代码@5,如果节点状态为 Node.SIGNAL,将状态设置为0,设置成功,唤醒线程。为什么会设置不成功,可能改节点被取消;还有一种情况就是有多个线程在运行该代码段,这就是PROPAGATE的含义吧,传播,请看代码@7的理解。
代码@6,如果状态为0,则设置为Node.PROPAGATE,设置为传播,该值然后会在什么时候变化呢?在判断该节点的下一个节点是否需要阻塞时,会判断,如果状态不是Node.SIGNAL或取消状态,为了保险起见,会将前置节点状态设置为Node.SIGNAL,然后再次判断,是否需要阻塞。
代码@7,如果处理过一次 unparkSuccessor 方法后,头节点没有发生变化,就退出该方法,那head在什么时候会改变呢?当然在是抢占锁成功的时候,head节点代表获取锁的节点。一旦获取锁成功,则又会进入setHeadAndPropagate方法,当然又会触发doReleaseShared方法,传播特性应该就是表现在这里吧。再想一下,同一时间,可以有多个多线程占有锁,那在锁释放时,写锁的释放比较简单,就是从头部节点下的第一个非取消节点,唤醒线程即可,为了在释放读锁的上下文环境中获取代表读锁的线程,将信息存入在 readHolds ThreadLocal变量中。
到这里为止,读锁的申请就讲解完毕了,先给出如下流程图:
尝试获取读锁过程
从队列中获取读锁的流程如下:
2.1.2 ReadLock 的 unlock方法详解
锁的释放,比较简单,代码@1,主要是将当前线程所持有的锁的数量信息得到(从firstReader或cachedHoldCounter,或readHolds中获取 ),然后将数量减少1,如果持有数为1,则直接将该线程变量从readHolds ThreadLocal变量中移除,避免垃圾堆积。
代码@2,就是在无限循环中将共享锁的数量减少一,在释放锁阶段,只有当所有的读锁,写锁被占有,才会去执行doReleaseShared方法。
2.2 WriterLock 源码分析
2.2.1 lock方法详解
代码@1,如果锁的state不为0,说明有写锁,或读锁,或两种锁持有
代码@2,如果写锁为0,再加上c!=0,说明此时有读锁,自然返回false,表示只能排队去获取写锁
如果写锁不为0,如果持有写锁的线程不为当前线程,自然返回false,排队去获取写锁。
代码@3,表示,当前线程持有写锁,现在是重入,所以只需要修改锁的额数量即可。
代码@4,表示,表示通过一次CAS去获取锁的时候失败,说明被别的线程抢去了,也返回false,排队去重试获取锁。
代码@5,成获取写锁后,将当前线程设置为占有写锁的线程。尝试获取锁方法结束。如果该方法返回false,则进入到acquireQueue方法去排队获取写锁,写锁的获取过程,与ReentrantLock获取方法一样,就不过多的解读了。
读写锁的实现原理就分析到这了,走过路过的朋友,欢迎拍砖讨论。
本文固定链接:
最新文章随机精彩热门排行
精彩内容获取超时,请稍候...
日志总数:3903 篇
评论总数:146 评
标签数量:4475 个
链接总数:4 条
建站日期:
运行天数:1441 天

我要回帖

更多关于 c readwritelock 的文章

 

随机推荐