前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >死磕到底-ReentrantReadWriteLock源码

死磕到底-ReentrantReadWriteLock源码

作者头像
大猫的Java笔记
发布2021-09-08 10:36:22
2840
发布2021-09-08 10:36:22
举报
文章被收录于专栏:大猫的Java笔记大猫的Java笔记

1.线程安全

如果对num进行累加操作,使用10个线程,每个加1000次,最后应该是10000,但是你会发现不是10000。

当使用了synchronized再次进行累加操作。此时累加的值就是10000,这是因为synchronized能够保证每次只有一个线程进入临界区。

2.ReentrantReadWriteLock

JDK中synchronized的使用是非常广泛的,例如线程安全的HashTable,HashTable保证线程安全就是在每个方法上都加上了synchronized。如下是HashTable的put和get方法。

对于HashTable来说,你发没发现一个问题就是实际上我们多个线程调用get方法的时候并不需要加锁,原因是如果不存在更改HashTable结构的操作,这样只是调用get方法但是加了synchronized。在多线程情况下并发度会大大降低。

那么有没有一种锁可以在读的时候不加锁,写的时候才加锁。同时写和写是冲突的,读和写也是冲突的,但是读和读是不会冲突的。这就是读写锁

ReentrantReadWriteLock。可以通过下面的例子看到实际上故意注释了解锁的unlock,所有的读锁还是能够进行获取资源。

写和读之间冲突,只有释放了写锁,读锁才能获取资源。下面代码只是方便演示,实际上解锁必须放在finally中,防止因为异常没有释放锁。其他线程阻塞的问题。

同样写和写也是冲突的,只有等写锁释放了其他线程才能够拿到写锁。

3.源码

读锁的lock源码

同首先调用lock会调用acquireShared方法,而acquireShared方法中调用了tryAcquireShared(尝试获取锁),只要获取成功了就返回1否则-1,也就是说只有获取锁失败才会执行doAcquireShared。

tryAcquireShared实现如下,需要注意的是读写锁采用了state来表示状态,高16位表示读状态数量,低16位表示写状态数量。

只要获取成功就会返回1也就是不会执行doAcquireShared,也只有获取失败才会执行doAcquireShared。doAcquireShared的基本实现如下。和之前说的ReentrantLock一样,读写锁会把需要阻塞的线程放入队列中。然后当可以获取锁的时候再进行唤醒操作。

读锁的unLock源码

首先调用releaseShared方法,然后releaseShared调用了tryReleaseShared方法,只有tryReleaseShared返回为true才会调用doReleaseShared方法,同时tryReleaseShared返回true表示锁已经释放完毕,包括重入的锁。

tryReleaseShared的实现如下,可以看到只有nextc == 0的时候也就是所有读线程已经释放完毕的时候才会返回true。

doReleaseShared可能你也猜到了需要干嘛,对于阻塞的线程我们都放在队列中,既然锁都释放完毕了那么肯定是唤醒他们。doReleaseShared实现如下。

到此在读锁到源码已经说完了,接下来就是写锁的源码

写锁的lock源码

写锁的lock首先调用acquire方法

acquire方法只有拿到锁或者重入成功后才会返回ture,也就是只有没有拿到锁或者没有重入成功才会执行才会执行acquireQueued(addWaiter(Node.EXCLUSIVE), arg)方法进行加入队列以及阻塞线程。

tryAcquire方法实现如下所示,可以看到只有重入成功或者加锁成功的时候才会返回true。

一旦加锁或者重入失败此时会先调用addWaiter方法加入等待队列中

加入队列后那么肯定是要阻塞线程,此时会调用acquireQueued方法,然后尝试再次获取锁,一旦失败会阻塞线程。

到此写锁的加锁源码结束。接下来是解锁过程。

写锁的unLock源码

到此写锁的加锁源码结束。接下来是解锁过程。解锁会先调用release方法,然后release方法会调用release方法。

通过tryRelease方法可以看到只有所有的写锁都释放完毕了才会进行唤醒,也就是调用exclusiceCount的时候等于0。一旦释放完毕此时release中会拿到头部节点然后唤醒队列头部的线程,此时你可能在想那写锁只是唤醒了头部获取读锁被阻塞的有多个,这些是怎么被唤醒的呢?实际上在读锁被阻塞中调用了doAcquireShared方法,一旦被释放,此时会将除了头部以外后续的节点都唤醒。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-08-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 大猫的Java笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档