读写锁ReentrantReadWriteLock源码分析

该方法是获取读锁被获取的次数,是将同步状态(int c)右移16次,即取同步状态的高16位,现在我们可以得出另外一个结论同步状态的高16位用来表示读锁被获取的次数。现在还记得我们开篇说的需要弄懂的第一个问题吗?读写锁是怎样实现分别记录读锁和写锁的状态的,现在这个问题的答案就已经被我们弄清楚了,其示意图如下图所示:

在这里插入图片描述

现在我们回过头来看写锁获取方法tryAcquire,其主要逻辑为:当读锁已经被读线程获取或者写锁已经被其他写线程获取,则写锁获取失败;否则,获取成功并支持重入,增加写状态

写锁的释放

写锁释放通过重写AQS的tryRelease方法,源码为:

protected final boolean tryRelease(int releases) {

if (!isHeldExclusively())

throw new IllegalMonitorStateException();

//1. 同步状态减去写状态

int nextc = getState() - releases;

//2. 当前写状态是否为0,为0则释放写锁

boolean free = exclusiveCount(nextc) == 0;

if (free)

setExclusiveOwnerThread(null);

//3. 不为0则更新同步状态

setState(nextc);

return free;

}

源码的实现逻辑请看注释,不难理解与ReentrantLock基本一致,这里需要注意的是,减少写状态int nextc = getState() - releases;只需要用当前同步状态直接减去写状态的原因正是我们刚才所说的写状态是由同步状态的低16位表示的

读锁详解


读锁的获取

看完了写锁,现在来看看读锁,读锁不是独占式锁,即同一时刻该锁可以被多个读线程获取也就是一种共享式锁。按照之前对AQS介绍,实现共享式同步组件的同步语义需要通过重写AQS的tryAcquireShared方法和tryReleaseShared方法。读锁的获取实现方法为:

protected final int tryAcquireShared(int unused) {

/*

  • Walkthrough:

    1. If write lock held by another thread, fail.
    1. Otherwise, this thread is eligible for
  • lock wrt state, so ask if it should block

  • because of queue policy. If not, try

  • to grant by CASing state and updating count.

  • Note that step does not check for reentrant

  • acquires, which is postponed to full version

  • to avoid having to check hold count in

  • the more typical non-reentrant case.

    1. If step 2 fails either because thread
  • apparently not eligible or CAS fails or count

  • saturated, chain to version with full retry loop.

*/

Thread current = Thread.currentThread();

int c = getState();

//1. 如果写锁已经被获取并且获取写锁的线程不是当前线程的话,当前

// 线程获取读锁失败返回-1

if (exclusiveCount© != 0 &&

getExclusiveOwnerThread() != current)

return -1;

int r = sharedCount©;

if (!readerShouldBlock() &&

r < MAX_COUNT &&

//2. 当前线程获取读锁

compareAndSetState(c, c + SHARED_UNIT)) {

//3. 下面的代码主要是新增的一些功能,比如getReadHoldCount()方法

//返回当前获取读锁的次数

if (r == 0) {

firstReader = current;

firstReaderHoldCount = 1;

} else if (firstReader == current) {

firstReaderHoldCount++;

} else {

HoldCounter rh = cachedHoldCounter;

if (rh == null || rh.tid != getThreadId(current))

cachedHoldCounter = rh = readHolds.get();

else if (rh.count == 0)

readHolds.set(rh);

rh.count++;

}

return 1;

}

//4. 处理在第二步中CAS操作失败的自旋已经实现重入性

return fullTryAcquireShared(current);

}

代码的逻辑请看注释,需要注意的是 当写锁被其他线程获取后,读锁获取失败,否则获取成功利用CAS更新同步状态。另外,当前同步状态需要加上SHARED_UNIT((1 << SHARED_SHIFT)即0x00010000)的原因这是我们在上面所说的同步状态的高16位用来表示读锁被获取的次数。如果CAS失败或者已经获取读锁的线程再次获取读锁时,是靠fullTryAcquireShared方法实现的,这段代码就不展开说了,有兴趣可以看看。

读锁的释放

读锁释放的实现主要通过方法tryReleaseShared,源码如下,主要逻辑请看注释:

protected final boolean tryReleaseShared(int unused) {

Thread current = Thread.currentThread();

// 前面还是为了实现getReadHoldCount等新功能

if (firstReader == current) {

// assert firstReaderHoldCount > 0;

if (firstReaderHoldCount == 1)

firstReader = null;

else

firstReaderHoldCount–;

} else {

HoldCounter rh = cachedHoldCounter;

if (rh == null || rh.tid != getThreadId(current))

rh = readHolds.get();

int count = rh.count;

if (count <= 1) {

readHolds.remove();

if (count <= 0)

throw unmatchedUnlockException();

}

–rh.count;

}

for (;😉 {

int c = getState();

// 读锁释放 将同步状态减去读状态即可

int nextc = c - SHARED_UNIT;

if (compareAndSetState(c, nextc))

// Releasing the read lock has no effect on readers,

// but it may allow waiting writers to proceed if

// both read and write locks are now free.

return nextc == 0;

}

}

锁降级


读写锁支持锁降级,遵循按照获取写锁,获取读锁再释放写锁的次序,写锁能够降级成为读锁,不支持锁升级,关于锁降级下面的示例代码摘自ReentrantWriteReadLock源码中:

void processCachedData() {

rwl.readLock().lock();

if (!cacheValid) {

// Must release read lock before acquiring write lock

rwl.readLock().unlock();

rwl.writeLock().lock();

try {

// Recheck state because another thread might have

// acquired write lock and changed state before we did.

if (!cacheValid) {

data = …

cacheValid = true;

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后希望可以帮助到大家!

千千万万要记得:多刷题!!多刷题!!

之前算法是我的硬伤,后面硬啃了好长一段时间才补回来,算法才是程序员的灵魂!!!!

篇幅有限,以下只能截图分享部分的资源!!

(1)多线程(这里以多线程为代表,其实整理了一本JAVA核心架构笔记集)

image

(2)刷的算法题(还有左神的算法笔记)

image

(3)面经+真题解析+对应的相关笔记(很全面)

image

(4)视频学习(部分)

ps:当你觉得学不进或者累了的时候,视频是个不错的选择

在这里,最后只一句话:祝大家offer拿到手软!!
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!
LQijH9M1-1712514506039)]

(2)刷的算法题(还有左神的算法笔记)

[外链图片转存中…(img-kwoXXHC7-1712514506039)]

(3)面经+真题解析+对应的相关笔记(很全面)

[外链图片转存中…(img-M0k1cTyi-1712514506039)]

(4)视频学习(部分)

ps:当你觉得学不进或者累了的时候,视频是个不错的选择

在这里,最后只一句话:祝大家offer拿到手软!!
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

  • 13
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值