acquire怎么读

更新时间:2022-11-23 16:23:59 阅读: 评论:0


2022年11月23日发(作者:在你背后)

华为应⽤锁退出⽴即锁_读写锁应⽤与原理

⽬录:

概念

应⽤

原理

StampedLock

⼀.概念

独占锁:指该锁⼀次只能被⼀个线程所持有。对ReentrantLock和Synchronized⽽⾔都是独占锁

共享锁:指该锁可以被多个线程锁持有

对ReentrantReadWriteLock其读锁是共享,其写锁是独占

写的时候只能⼀个⼈写,但是读的时候,可以多个⼈同时读

为什么会有写锁和读锁

原来我们使⽤ReentrantLock创建锁的时候,是独占锁,也就是说⼀次只能⼀个线程访问,但是有⼀个读写分离场景,读的时候想同时进

⾏,因此原来独占锁的并发性就没这么好了,因为读锁并不会造成数据不⼀致的问题,因此可以多个⼈共享读

多个线程同时读⼀个资源类没有任何问题,所以为了满⾜并发量,读取共享资源应该可以同时进⾏,但是如果⼀个线程想去写共享资源,

就不应该再有其它线程可以对该资源进⾏读或写

读-读:能共存

读-写:不能共存

写-写:不能共存

ReentrantReadWriteLock

当读操作远远⾼于写操作时,这时候使⽤读写锁让读-读可以并发,提⾼性能。类似于数据库中的lect...from...lockinshare

mode

提供⼀个数据容器类内部分别使⽤读锁保护数据的read()⽅法,写锁保护数据的write()⽅法

代码

classDataContainer{privateObjectdata;privateReentrantReadWriteLockrw=newReentrantReadWriteLock();ckr

测试读锁-读锁可以并发

DataContainerdataContainer=newDataContainer();newThread(()->{();},"t1").start();newThread(()->{();},"t2").star

输出结果,从这⾥可以看到Thread-0锁定期间,Thread-1的读操作不受影响

14:05:ntainer[t2]-获取读锁...14:05:ntainer[t1]-获取读锁...14:05:ntainer[t1]-读取14:05:nt

测试读锁-写锁相互阻塞

DataContainerdataContainer=newDataContainer();newThread(()->{();},"t1").start();(100);newThread(()->{dataContaine

输出结果

14:04:ntainer[t1]-获取读锁...14:04:ntainer[t2]-获取写锁...14:04:ntainer[t2]-写⼊14:04:nt

写锁-写锁也是相互阻塞的,这⾥就不测试了

注意事项

读锁不⽀持条件变量

重⼊时升级不⽀持:即持有读锁的情况下去获取写锁,会导致获取写锁永久等待

();try{//...();try{//...}finally{();}}finally{();}

重⼊时降级⽀持:即持有写锁的情况下去获取读锁

classCachedData{Objectdata;//是否有效,如果失效,需要重新计算datavolatilebooleancacheValid;finalReentrantReadWriteLockrwl=newReentrantRe

⼆.读写锁应⽤

1.缓存更新策略更新时,是先清缓存还是先更新数据库

先清缓存

先更新数据库

补充⼀种情况,假设查询线程A查询数据时恰好缓存数据由于时间到期失效,或是第⼀次查询

这种情况的出现⼏率⾮常⼩,见facebook论⽂

2.读写锁实现⼀致性缓存

使⽤读写锁实现⼀个简单的按需加载缓存

classGenericCachedDao{//HashMap作为缓存⾮线程安全,需要保护HashMapmap=newHashMap<>();ReentrantReadWriteLocklock=newReentrantRea

注意

以上实现体现的是读写锁的应⽤,保证缓存和数据库的⼀致性,但有下⾯的问题没有考虑

适合读多写少,如果写操作⽐较频繁,以上实现性能低没有考虑缓存容量

没有考虑缓存过期

只适合单机

并发性还是低,⽬前只会⽤⼀把锁

更新⽅法太过简单粗暴,清空了所有key(考虑按类型分区或重新设计key)

乐观锁实现:⽤CAS去更新

三.读写锁原理

1.图解流程

读写锁⽤的是同⼀个Sycn同步器,因此等待队列、state等也是同⼀个

1)t1成功上锁,流程与ReentrantLock加锁相⽐没有特殊之处,不同是写锁状态占了state的低16位,⽽读锁使⽤的是state的⾼

16位

2)t2执⾏,这时进⼊读锁的eShared(1)流程,⾸先会进⼊tryAcquireShared流程。如果有写锁占据,那么

tryAcquireShared返回-1表⽰失败

tryAcquireShared返回值表⽰

-1表⽰失败

0表⽰成功,但后继节点不会继续唤醒

正数表⽰成功,⽽且数值是还有⼏个后继节点需要唤醒,读写锁返回1

3)这时会进⼊ireShared(1)流程,⾸先也是调⽤addWaiter添加节点,不同之处在于节点被设置为模式

⽽⾮IVE模式,注意此时t2仍处于活跃状态

4)t2会看看⾃⼰的节点是不是⽼⼆,如果是,还会再次调⽤tryAcquireShared(1)来尝试获取锁

5)如果没有成功,在doAcquireShared内for(;;)循环⼀次,把前驱节点的waitStatus改为-1,再for(;;)循环⼀次尝试

tryAcquireShared(1)如果还不成功,那么在parkAndCheckInterrupt()处park

这种状态下,假设⼜有t3加读锁和t4加写锁,这期间t1仍然持有锁,就变成了下⾯的样⼦

这时会⾛到写锁的e(1)流程,调⽤ea(1)成功,变成下⾯的样⼦

接下来执⾏唤醒流程Successor,即让⽼⼆恢复运⾏,这时t2在doAcquireShared内parkAndCheckInterrupt()处恢复

运⾏

这回再来⼀次for(;;)执⾏tryAcquireShared成功则让读锁计数加⼀

这时t2已经恢复运⾏,接下来t2调⽤tHeadAndPropagate(node,1),它原本所在节点被置为头节点

事情还没完,在tHeadAndPropagate⽅法内还会检查下⼀个节点是否是shared,如果是则调⽤doReleaShared()将head的状态

从-1改为0并唤醒⽼⼆,这时t3在doAcquireShared内parkAndCheckInterrupt()处恢复运⾏

这回再来⼀次for(;;)执⾏tryAcquireShared成功则让读锁计数加⼀

这时t3已经恢复运⾏,接下来t3调⽤tHeadAndPropagate(node,1),它原本所在节点被置为头节点

下⼀个节点不是shared了,因此不会继续唤醒t4所在节点,

t2进⼊eShared(1)中,调⽤tryReleaShared(1)让计数减⼀,但由于计数还不为零

t3进⼊eShared(1)中,调⽤tryReleaShared(1)让计数减⼀,这回计数为零了,进⼊doReleaShared()将头节点从-

1改为0并唤醒⽼⼆,即

之后t4在acquireQueued中parkAndCheckInterrupt处恢复运⾏,再次for(;;)这次⾃⼰是⽼⼆,并且没有其他竞

争,tryAcquire(1)成功,修改头结点,流程结束

2.源码分析

写锁上锁流程

staticfinalclassNonfairSyncextendsSync{//...省略⽆关代码//外部类WriteLock⽅法,⽅便阅读,放在此处publicvoidlock(){e(1);}//AQS继承

写锁释放流程

staticfinalclassNonfairSyncextendsSync{//...省略⽆关代码//WriteLock⽅法,⽅便阅读,放在此处publicvoidunlock(){e(1);}//AQS继承过

读锁上锁流程

staticfinalclassNonfairSyncextendsSync{//ReadLock⽅法,⽅便阅读,放在此处publicvoidlock(){eShared(1);}//AQS继承过来的⽅法,⽅

读锁释放流程

staticfinalclassNonfairSyncextendsSync{//ReadLock⽅法,⽅便阅读,放在此处publicvoidunlock(){eShared(1);}//AQS继承过来的⽅法,⽅

StampedLock

该类⾃JDK8加⼊,是为了进⼀步优化读性能,它的特点是在使⽤读锁、写锁时都必须配合【戳】使⽤

加解读锁

longstamp=ck();Read(stamp);

加解写锁

longstamp=ock();Write(stamp);

乐观读,StampedLock⽀持tryOptimisticRead()⽅法(乐观读),读取完毕后需要做⼀次戳校验如果校验通过,表⽰这期间确实没有写

操作,数据可以安全使⽤,如果校验没通过,需要重新获取读锁,保证数据安全。

longstamp=imisticRead();//验戳if(!te(stamp)){//锁升级}

提供⼀个数据容器类内部分别使⽤读锁保护数据的read()⽅法,写锁保护数据的write()⽅法

classDataContainerStamped{privateintdata;privatefinalStampedLocklock=newStampedLock();publicDataContainerStamped(intdata){

测试读-读可以优化

publicstaticvoidmain(String[]args){DataContainerStampeddataContainer=newDataContainerStamped(1);newThread(()->{(1);},

输出结果,可以看到实际没有加读锁

15:58:ntainerStamped[t1]-optimisticreadlocking...25615:58:ntainerStamped[t2]-optimisticreadlocking...25615:58

测试读-写时优化读补加读锁

publicstaticvoidmain(String[]args){DataContainerStampeddataContainer=newDataContainerStamped(1);newThread(()->{(1);},"

输出结果

15:57:ntainerStamped[t1]-optimisticreadlocking...25615:57:ntainerStamped[t2]-writelock38415:57:

注意StampedLock不⽀持条件变量

StampedLock不⽀持可重⼊

本文发布于:2022-11-23 16:23:59,感谢您对本站的认可!

本文链接:http://www.wtabcd.cn/fanwen/fan/90/6813.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

下一篇:seriously
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图