文档章节

读写锁

草根本色
 草根本色
发布于 2015/12/02 10:41
字数 1120
阅读 12
收藏 0

读锁为共享锁,而写锁则为独占锁,同时他们用的是一个同步器,同步器中不同的内部类实现了相应的功能


在AQS章节中介绍到AQS中有一个state字段(int类型,32位)用来描述有多少线程获持有锁。在独占锁的时代这个值通常是0或者1(如果是重入的就是重入的次数),在共享锁的时代就是持有锁的数量。在上一节中谈到,ReadWriteLock的读、写锁是相关但是又不一致的,所以需要两个数来描述读锁(共享锁)和写锁(独占锁)的数量。显然现在一个state就不够用了。于是在ReentrantReadWrilteLock里面将这个字段一分为二,高位16位表示共享锁的数量,低位16位表示独占锁的数量(或者重入数量)。2^16-1=65536,这就是上节中提到的为什么共享锁和独占锁的数量最大只能是65535的原因了。

1.在独占数量上则采用了return c & EXCLUSIVE_MASK  

EXCLUSIVE_MASK  = (1 << SHARED_SHIFT) - 1; 表示向左移动16位 然后减掉1,即低16位全为1,高16位全为0

即通过 & 000(....)111(....)的形式获取低16位

通过与操作就能知道states变量的低16的值,也即独占锁数量

2.共享锁数量上则采用了往右移16位的形式,最高位都补0,

即111111(...)变成了类似000111(...)的形式获取高16位


写锁的获取  

一、

    c!=0说明有锁了,

    1.w==0成立说明有多个读锁在用了,这时不能获取写锁,因为写锁将导致数据乱掉

    2.w!=0 成立 说明当前有写锁,如果该线程进来竞争写锁又不是当前线程的话则不行,他们是独占的,同时如果写锁进来后加起来超过了65535(即16位都为1的情况下再进来一个写锁的话)则报错,超出了能记录的锁数量限制


二、

    c==0说明没有任何的锁,

   前面判断写是不是需要阻塞(这个是公平锁和非公平锁的阻塞情况),

如果AQS队列不为空或者当前线程不在队列头部那么就阻塞队列(即加入到同步队列中自身不断自旋)

后面判断获取写锁,失败直接false,其他则true


写锁的释放

判断是否存在重入的情况,如果是重入的话不能将当前的线程信息清空


读锁的获取

1.首先判断是否存在写锁 同时当前线程是否为那个线程,如果不是拜拜,如果是的话则到2(允许写锁的线程同时获取读锁)

2.读是否阻塞,读锁数量有没有超过,那么读锁就CAS +1,如果判断没通过自旋去吧


读锁释放

不断的自旋CAS


读锁里面存在一个特别的HoldCounter,因为读锁是共享的,存在多个读锁的情况,同时释放的时候其实是类似计数器的形式,为了避免一个线程操作了一个不属于自己监听器对象的异常,也就是一个线程释放了一个不属于自己的或不存在的共享锁就引入了HoldCounter

HoldCounter的作用后我们就可以猜到它的作用其实就是当前线程持有共享锁(读取锁)的数量,包括重入的数量。那么这个数量就必须和线程绑定在一起,表示线程持有读取锁数量的计数器。可以看到这里使用ThreadLocal将HoldCounter绑定到当前线程上,同时HoldCounter也持有线程Id,这样在释放锁的时候才能知道ReadWriteLock里面缓存的上一个读取线程(cachedHoldCounter)是否是当前线程。这样做的好处是可以减少ThreadLocal.get()的次数,因为这也是一个耗时操作。需要说明的是这样HoldCounter绑定线程id而不绑定线程对象的原因是避免HoldCounter和ThreadLocal互相绑定而GC难以释放它们(尽管GC能够智能的发现这种引用而回收它们,但是这需要一定的代价),所以其实这样做只是为了帮助GC快速回收对象而已。


© 著作权归作者所有

草根本色
粉丝 1
博文 24
码字总数 4631
作品 0
海淀
程序员
私信 提问

暂无文章

RxJava进行单元测试的方式

@Test public void completeTask_retrievedTaskIsComplete() { // Given a new task in the persistent repository final Task newTask = new Task(TITLE, ""); ......

SuShine
2分钟前
1
0
正则表达式大全

检验手机号码 # 要求:手机号码必须为11位数字,以1开头,第二位为1或5或8。import redef verify_mobile(): mob = input("请输入手机号码:") ret = re.match(r"1[358]\d{9}", m......

彩色泡泡糖
6分钟前
1
0
QT之border-image属性

一、border-image的兼容性 border-image可以说是CSS3中的一员大将,将来一定会大放光彩,其应用潜力真的是非常的惊人。可惜目前支持的浏览器有限,仅Firefox3.5,chrome浏览器,Safari3+支持...

shzwork
6分钟前
2
0
Kubernetes Operator简易教程

1. 安装operator-sdk //安装 operator-sdk$ apt-get install operator-sdk.....$ operator-sdk versionoperator-sdk version: v0.7.0$ go versiongo version go1.11.4 darwin/amd64 2......

Robotcl_Blog
6分钟前
2
0
再谈DAG任务分解和Shuffle RDD

1、DagScheduler分析 DagScheduler功能主要是负责RDD的各个stage的分解和任务提交。Stage分解是从触发任务调度过程的finalStage开始倒推寻找父stage,如果父stage没有提交任务则循环提交缺失...

守望者之父
12分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部