reentrantreadwritelock通过state变量高16位记读锁次数、低16位记写锁重入次数,实现读共享、写独占;写锁要求读写均空闲才可获取,读锁仅需写锁空闲即可并发进入,且依赖threadlocalholdcounter支持读锁重入。

ReentrantReadWriteLock 通过分离读锁和写锁的计数与状态管理,实现读锁共享、写锁独占。核心在于用一个 int 类型的同步状态变量(state)同时编码读锁与写锁的持有数量,并借助 AQS 的 wait queue 和 condition queue 协调线程排队与唤醒。
state 状态的巧妙拆分
ReentrantReadWriteLock 将 AQS 的 int 型 state 拆为两部分:高 16 位表示读锁持有次数(shared count),低 16 位表示写锁重入次数(exclusive count)。这种设计让读写状态共用一个 volatile 变量,避免额外同步开销。
- 写锁获取时,检查 state 低 16 位是否为 0(无其他写锁),且高 16 位为 0(无任何读锁)——满足才可抢占
- 读锁获取时,只要写锁未被持有(即低 16 位为 0),就允许多个线程同时成功,各自增加高 16 位计数
- 写锁释放后,若 state 回到 0,说明读写锁都空闲,可能唤醒等待中的读线程或写线程
读锁的共享机制
读锁不是真正“无锁”,而是允许多个线程同时进入临界区的**共享锁**。它依赖 state 高位计数 + 读线程本地计数(ThreadLocalHoldCounter)来支持重入和释放匹配。
- 每个读线程首次获取读锁时,在 ThreadLocal 中记录自己持有的读锁次数;后续重入直接递增本地计数
- 释放时先减本地计数,仅当本地计数归零才真正减少 state 高 16 位
- 即使多个线程持有读锁,只要没写锁在场,它们都能并发执行读操作
写锁的独占保障
写锁是标准的可重入独占锁,完全遵循 AQS 的 exclusive 模式。一旦有线程持有了写锁,所有读锁和写锁请求都会被阻塞,直到该写锁释放。
- 写锁获取失败时,线程会进入 AQS 同步队列排队,按 FIFO 顺序竞争
- 写锁支持重入:同一线程可多次 lock(),state 低 16 位递增;每次 unlock() 对应一次递减
- 写锁释放后,会检查是否有等待的写线程(优先唤醒),再考虑唤醒一批读线程(避免写饥饿)
读写锁之间的互斥规则
ReentrantReadWriteLock 明确规定三类互斥关系:
- 写-写:互斥,不可并发
- 写-读:互斥,写锁存在时,新读锁请求必须等待
- 读-读:不互斥,多个读线程可同时持有读锁
注意:它不保证写操作的“可见性”自动传播——读线程仍需依赖 volatile 或 synchronized 确保看到写线程的最新值;锁本身只控制临界区进入权限,不替代内存模型语义。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











