源码级丨从 jdk 的 git 提交记录告诉你 AQS 读写锁 为什么需要特意维护 firstReader 和 cacheHoldCounter

源码篇 预防针💉:

源码篇 必然会有很多代码,希望大家不要有畏难情绪,虽然我也是看到一大串代码就头疼。 我贴代码只是为了方便我的文字解答,源码只是辅助我文字讲解,所以大家尽量关注我的文字就好啦。

前言

在上一篇文章中,我们得知了 ReentrantReadWriteLock 给每个线程都维护了一个 ThreadLocal 类型的 HoldCount 对象,用来维护每个线程的读锁重入次数。

然后我留下了一些疑问:

  • 为什么每个线程都维护了 HoldCounter,还需要额外维护一个 firstReader ?

  • 为什么需要特意维护 CacheHoldCounter?

为什么需要特意维护 firstReader 和 cacheHoldCount

为什么 ReentrantReadWriteLock 特意维护了 第一个线程 和 最后一个 获取锁成功的线程?

只维护头和尾能带来性能上的提升吗?具体体现在哪里?

ThreadLocal 的查询

我们先来看 为什么需要 cacheHoldCount

在源码 fullTryAcquireShared 方法中其实我们可以看到有这么一个注释:

显然 cacheHoldCounter 是为了 释放锁 而特意维护的。

cacheHoldCounter 究竟能帮助我们怎么去优化 释放锁 呢?

看到这里基本上就是真相大白了。

cachedHoldCounter 是用来优化 锁释放时ThreadLocal 查找的。

而且这里提到了一个普遍的例子就是:通常来说需要释放的线程,就是最后一个获取锁的线程

在这种场景下,我们就不需要去通过 ThreadLocal 去查,而是直接通过我们这个 cacheHoldCounter

那为什么需要维护 firstReader 捏?

通过注释其实是不太好找到线索的。

但是我们可以先确定的一件事就是,firstReader 肯定能起到跟 cachHoldCounter 的作用,也就是在优化了在 ThreadLocal 的查找。

tryReleaseShared 读锁释放逻辑:

ThreadLocal 的内存溢出风险

我们通过 jdkgit 历史提交记录可以看到,这个 firstReader 其实是后来才加进来的。

Excessive ThreadLocal storage used by ReentrantReadWriteLock

大概意思就是:ReentrantReadWriteLockThreadLocal 太多了。

ThreadLocal 虽然是一个好东西,但是它有一个非常大的问题就是它存在 内存溢出 的风险。

现在多加了一个 firstReader,相当于是一把 ReentrantReadWriteLock 就优化一个 ThreadLocal 变量。

积少成多,也是对性能比较极致的追求。

而且我们还可以看到这个提交记录中还包括了对锁中的 ThreadLocal 对象手动清除: readHolds.remove()

这属于是一个 bug,也是在这次提交中修复了。

感兴趣的大兄弟可以去具体 oracle 官网查看:Bug ID: JDK-6625723 Excessive ThreadLocal storage used by ReentrantReadWriteLock (java.com)

总结

为什么需要 cacheHoldCounter ?

cacheHoldCounter 是维护最后一个成功获取到锁的线程。

维护它是为了在 锁释放 中减少对于 ThreadLocal 的查询。

为什么需要 firstReader

目的是为了优化 读写锁 中对 ThreadLocal 的过度使用导致内存溢出问题。

在早期设计 读写锁 中是没有 firstReader,后来人们提出他对于 ThreadLocal 的使用已经达到了 滥用 这个级别。

但是又不得不用 ThreadLocal,所以 Doug Lea 采用了维护一个 firstReader 的方法,尽可能地去减少这个内存的占用。

同时 fistReader锁释放 的时候也可以优化 ThreadLocal 的查询。

参考

来都来了,点个赞,留个言再走吧彦祖👍,这对我来说非常重要!

相关推荐
程序员游老板44 分钟前
基于SpringBoot3_vue3_MybatisPlus_Mysql_Maven的社区养老系统/养老院管理系统
java·spring boot·mysql·毕业设计·软件工程·信息与通信·毕设
福尔摩斯张1 小时前
C++核心特性精讲:从C语言痛点出发,掌握现代C++编程精髓(超详细)
java·linux·c语言·数据结构·c++·驱动开发·算法
@淡 定1 小时前
Spring中@Autowired注解的实现原理
java·后端·spring
前端一小卒1 小时前
一个看似“送分”的需求为何翻车?——前端状态机实战指南
前端·javascript·面试
时空无限1 小时前
Java Buildpack Reference
java·开发语言
xlp666hub2 小时前
C进阶之内存对齐,硬件总线和高并发伪共享的底层原理
面试·代码规范
xhxxx2 小时前
从被追问到被点赞:我靠“哨兵+快慢指针”展示了面试官真正想看的代码思维
javascript·算法·面试
爱笑的眼睛112 小时前
超越剪枝与量化:下一代AI模型压缩工具的技术演进与实践
java·人工智能·python·ai
yaoh.wang2 小时前
力扣(LeetCode) 14: 最长公共前缀 - 解法思路
python·程序人生·算法·leetcode·面试·职场和发展·跳槽
阿里云云原生2 小时前
Android App 崩溃排查指南:阿里云 RUM 如何让你快速从告警到定位根因?
android·java