ReenTrantReadWriteLock

2020-08-04T12:05:52
关注公众号【好便宜】( ID:haopianyi222 ),领红包啦~
阿里云,国内最大的云服务商,注册就送数千元优惠券:https://t.cn/AiQe5A0g
腾讯云,良心云,价格优惠: https://t.cn/AieHwwKl
搬瓦工,CN2 GIA 优质线路,搭梯子、海外建站推荐: https://t.cn/AieHwfX9

读写锁就是一种经常提到的场景,需要频繁的读还是不需要频繁的写,所以读是共享锁,但是写是排他锁。

要理解读写锁的源码先要理解:

读写锁的state=0代表的是当前没有写锁and读锁了。

1.

首先看类:,实现了ReadWriteLock接口,内部有5个内部类,Sync,FairSync,NoFairSync,ReadLock,WriteLock  ,  ReenTrantLock的时候说过Fair Lock和NoFairLock都继承至Sync, 而Sync继承值AbstractQueuedSynchronizer,所以这个还是基于AQS实现,ReadLock和WriteLock继承自Lock。

 

2.

接着要说一下读线程计数和写线程计数,定义很容易理解,因为读是共享锁,所以获取读线程数为sharedCount(),写是独占锁排他锁,所以为exclusiveCount()。  高十六位为读,低十六位为写,所以这么就可以获取到,我表示也不懂其中的原理, 但是这样解释是没错的0.0。

3.

接下来就比较简单了,直接看怎么获取锁,分为四种。

尝试获取写锁:tryAcquire

说明:先判断状态是不是0,如果是0说明没有读锁也没有写锁,直接判断一下写锁是不是阻塞了,没有阻塞则直接就设置占有线程为自己;如果不是0,则要判断写锁数量是不是0或者自己是不是当前占有线程(这里主要是为了可重入,如果写锁不是0并且是自己还是允许获取锁,也就是锁的可重入性,如果不是自己,那就不允许往下走,这也就是排他性。之后会调用AQS的方法添加到CLH队列走AQS的一套),直接返回false,如果上一步没问题,则判断数量有没有超过最大值,如果没有超时直接设置状态。

尝试释放写锁:

说明:free=0,表示释放锁成功,所以直接设置占有线程为null,然后设置状态

尝试获取读锁:tryAcquireShared()

说明:上来先判断有没有写锁(这个地方是实现锁的降级的关键,因为有写锁且是当前线程才能往下走),如果就不用往下走了,然后再判断读锁数量有没有超过最大值,如果数量是0,就设置自己是第一个读线程,如果自己已经是第一个了,那就计数器+1,都不满足就把自己设置一下并让计数器+1

尝试释放读锁:tryReleaseShared()

说明:先判断自己是不是第一个读线程,是的话就判断一下数量只有1就设置成null,不是就计数器-1; 如果不是第一个,就获取线程数,然后删掉本地里面的一个,然后设置状态。

5.前面4点都是调用AQS之前的判断,真正做操作的还是AQS的acquire和unparkSuccessor()

上面主要的方法就介绍完了,还有一个就是锁的降级:指的是一个线程已经获取了写锁,再获取读锁,然后再释放写锁,这时候线程就变成了一个读锁,所以就叫降级。这很好理解,因为写锁只有一个,所以他自己本身是读写锁不会影响数据的可见性,而且其他的读线程刚才也看过了,如果写线程数量不是0的话,就直接不往下走了,所以这时候写线程执行完再释放不会产生数据问题。这也解释为什么读写锁适合多读少写,因为有一个写所有的读都要等待。

然后说点题外话,读写锁不能升级。因为拥有读锁,可能别的线程同时拥有读锁,这时候获取写锁更新数据,别的读锁感受不到。

 

扫一扫关注公众号添加购物返利助手,领红包
当前页面是本站的「Baidu MIP」版。发表评论请点击:完整版 »
因本文不是用Markdown格式的编辑器书写的,转换的页面可能不符合MIP标准。