reentrantreadwritelock读锁通过state高16位与holdcounter协同实现重入:前者统计持有读锁的线程总数,后者记录各线程自身重入次数,兼顾共享性与可重入性。

ReentrantReadWriteLock 的读锁支持重入,但它的重入机制和写锁不同——不是靠单一 state 值直接累加,而是采用“双计数”协同设计:既记录全局读锁持有线程数(state 高16位),又为每个线程单独维护重入次数(HoldCounter)。
state 高16位:记录总读锁持有线程数
每次有线程成功获取读锁,state 的高16位就 +1;释放时 -1。这个值反映的是“当前有多少个线程持有读锁”,而非某个线程重入了多少次。它保证了读锁的共享性基础:只要没有写锁占用,多个线程可同时进入。
- 例如:线程A、B、C各自获取一次读锁,state 变为 0x00030000(十进制 196608)
- 若A再次获取(重入),state 变为 0x00040000,仍表示“4个读锁持有者”
HoldCounter:每个线程专属的重入计数器
仅靠 state 高16位无法区分“是新线程进入”还是“老线程重入”。为此,ReentrantReadWriteLock 为每个获取过读锁的线程,在 ThreadLocal 中缓存一个 HoldCounter 对象,其中 count 字段记录该线程当前重入次数。
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- 首次获取读锁时,创建 HoldCounter 并设 count = 1,同时放入 ThreadLocal
- 同一线程再次调用 readLock.lock(),只更新自身 HoldCounter.count += 1,state 高16位也 +1
- 释放时,先减 HoldCounter.count;归零后才从 ThreadLocal 清除,并让 state 高16位 -1
为什么需要两个计数器?
这是为了兼顾“共享性”与“可重入性”的双重需求:
- state 高16位用于 AQS 共享模式下的全局协调(比如判断是否允许新读线程进入)
- HoldCounter 保证单个线程能多次 lock/unlock 而不阻塞自己,且释放次数必须匹配获取次数
- 避免把所有重入都堆在 state 上——否则无法区分“10个线程各持1次”和“1个线程持10次”,会破坏读锁的并发语义
重入的典型流程(以线程A为例)
假设线程A已持读锁1次,再次调用 readLock.lock():
- 检查当前无写锁占用,且等待队列头部无待唤醒的写线程(公平模式下还需检查队列)
- 从 ThreadLocal 获取自己的 HoldCounter,发现 count = 1 → 执行 count++
- 调用 setState(state + (1
- 成功返回,不阻塞
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










