Java ReentrantReadWriteLock读写锁如何实现


这篇文章主要介绍了JavaReentrantReadWriteLock读写锁如何实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇JavaReentrantReadWriteLock读写锁如何实现文章都会有所收获,下面我们一起来看看吧。ReentrantReadWriteLock读写锁是使用AQS的集大成者,用了独占模式和共享模式。上图是ReentrantReadWriteLock读写锁的类结构图:实现了ReadWriteLock接口,该接口提供了获取读锁和写锁的API。ReentrantReadWriteLock读写锁内部的成员变量readLock是读锁,指向内部类ReadLock。ReentrantReadWriteLock读写锁内部的成员变量writeLock是写锁,指向内部类WriteLock。ReentrantReadWriteLock读写锁内部的成员变量sync是继承AQS的同步器,他有两个子类FairSync公平同步器和NoFairSync非公平同步器,读写锁内部也有一个sync,他们使用的是同一个sync。读写锁用的同一个sync同步器,那么他们共享同一个state, 这样不会混淆吗?不会,ReentrantReadWriteLock读写锁使用了AQS中state值得低16位表示写锁得计数,用高16位表示读锁得计数,这样就可以使用同一个AQS同时管理读锁和写锁。1.ReentrantReadWriteLock类重要成员变量2.ReentrantReadWriteLock构造方法3.Sync类重要成员变量设计一个加锁场景,t1线程加写锁,t2线程加读锁,我们看下它们整个加锁得流程。1.t1 加写锁w.lock()成功,占了 state 的低 16 位。这里得state分为两部分0_1,0表示高16位的值,1表示低16位的值。AQS当前占用线程exclusiveOwnerThread属性指向t1线程。2.t2线程执行加读锁 r.lock(),尝试获取锁,发现已经被写锁占据了,加锁失败。3.t2线程被封装成一个共享模式Node.SHARED的节点,加入到AQS的队列中。4.在阻塞前,t2线程发现自己是队列中的老二,会尝试再次获取读锁,因为t1没有释放,它会失败,然后它会把队列的前驱节点的状态改为-1,然后阻塞自身,也就是t2线程。上面中黄色三角形就是等待状态的值,前驱节点变成-1上面中的灰色表示节点所在的线程阻塞了5.后面如过有其他线程如t3,t4加读锁或者写锁,由于t1线程没有释放锁,会变成下面的状态。上面是整个解锁的流程,下面深入源码验证这个流程。1.写锁加锁源码WriteLock类的lock()方法是加写锁的入口方法。tryAcquire()方法是模板方法,由子类自定义实现获取锁的逻辑。线程如果获取写锁失败的话,通过acquireQueued()方法封装成独占Node加入到AQS队列中。2.读锁加锁源码ReadLock类的lock()方法是加读锁的入口方法,调用tryAcquireShared()方法尝试获取读锁,返回负数,失败,加入到队列中。tryAcquireShared()方法是一个模板方法,AQS类中定义语义,子类实现,如果返回1,表示获取锁成功,还有剩余资源,返回0表示获取成功,没有剩余资源,返回-1表示失败。fullTryAcquireShared()方法是通过自旋的方式不断获取读锁,因为由于前面的readerShouldBlock返回false或者cas失败,导致没有获取到锁,需要不断重试。doAcquireShared()是在获取读锁失败的时候加入AQS队列的逻辑。setHeadAndPropagate()方法是在后续读锁被唤醒后,抢到锁要处理的逻辑,包括修改队列的头结点,以及唤醒队列中的下一个共享节点。由于上面t1线程加的写锁,所有其他的线程都被阻塞了,只有在t1线程解锁以后,其他线程才能被唤醒,我们现在看下t1线程被唤醒了,会发生什么?1.t1线程执行解锁w.unlock()成功,修改AQS中的state。这里的state变为了0_0。AQS当前占用线程exclusiveOwnerThread属性变为null。2.t1线程唤醒队列中等待的老二, 为什么不是老大,因为老大是一个空节点,不会设置任何的线程。t2线程被唤醒后,抢锁成功,修改state中高16位为1。老二的线程节点变为蓝色节点AQS中的state变为1_0。3.t2线程恢复运行,设置原来的老二节点为头节点4.t2线程要做的事情还没结束呢,因为是共享模式,它现在释放了,就此时也唤醒队列中的下一个共享节点。5.t3线程恢复去竞争读锁成功,这时state的高位+1,变成2。6.这时候t3线程所在的Node设置为头节点,同时发现对列的下一个节点不是共享节点,而是独占节点,就不会唤醒后面的节点了。7.之后t2线程和t3线程进入尾声,执行r.unlock操作,state的计数减一,直到变为0。8.最后写锁线程t4被唤醒,去抢占锁成功,整个流程结束。上面是整个解锁的流程,下面深入源码验证这个流程。1.写锁释放流程WriteLock类的unlock()方法是入口方法,调用tryRelease()方法释放锁,如果成功,调用unparkSuccessor()方法唤醒线程。tryRelease()方法是AQS提供的模板方法,返回true表示成功,false失败,由自定义同步器实现。2.读锁释放流程R免费云主机域名eadLock类的unlock()方法是释放共享锁的入口方法。tryReleaseShared()方法是由AQS提供的模板方法,由自定义同步器实现。调用doReleaseShared()方法唤醒等待的线程,这个方法调用的地方有两处,还记得吗,一个这是里的解锁,还有一个是前面加共享锁阻塞的地方,唤醒后获取锁成功,也会调用doReleaseShared()方法。关于“JavaReentrantReadWriteLock读写锁如何实现”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“JavaReentrantReadWriteLock读写锁如何实现”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注百云主机行业资讯频道。

相关推荐: javascript是不是java的子集

这篇文章主要讲解了“javascript是不是java的子集”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“javascript是不是java的子集”吧! 不是。javascript和java是两套完全不同的语言…

免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。

(0)
打赏 微信扫一扫 微信扫一扫
上一篇 02/10 23:48
下一篇 02/11 16:52

相关推荐