源码级丨从 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 的查询。

参考

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

相关推荐
凡人叶枫11 小时前
Effective C++ 条款39:明智而审慎地使用 private 继承
java·数据库·c++·嵌入式开发
星哥的编程之路11 小时前
别再调 API 就说自己会 RAG 了,看看真正的企业级 AI 智能体长什么样
后端·面试
轻刀快马11 小时前
跨越软硬件的共鸣(二):从 Cache 写策略看 Redis 与 DB 的一致性博弈
java·开发语言·redis·计算机组成原理
折哥的程序人生 · 物流技术专研11 小时前
Java 23 种设计模式:从踩坑到精通 | 装饰器模式 —— 比继承更灵活的扩展方式,你用过吗?
java·装饰器模式·java面试·结构型模式·java设计模式·javaio·从踩坑到精通
lili001211 小时前
2026 企业 AI 选型新范式:OpenRouter Fusion 证明多模型融合性价比远超单模型,企业该如何重构技术栈? - 微元算力(weytoken)
java·人工智能·python·重构·ai编程
shushangyun_11 小时前
汽车服务行业B2B平台+AI解决方案哪家专业:2026年最新测评
java·运维·网络·数据库·人工智能·汽车
A.说学逗唱的Coke11 小时前
【大模型专题】Spring AI Alibaba × Skill 整合实战:让 AI 真正“会干活
java·人工智能·spring
大黄说说12 小时前
深入理解 Go 协程 Goroutine:并发编程的核心精髓
java·数据库·python
许彰午12 小时前
38_Java设计模式之装饰器模式
java·设计模式·装饰器模式
折哥的程序人生 · 物流技术专研12 小时前
Java 23 种设计模式:从踩坑到精通 | 组合模式 —— 树形结构处理,部分与整体一视同仁
java·组合模式·java面试·springsecurity·结构型模式·java设计模式·从踩坑到精通