锁降级在ReentrantReadWriteLock中并非真正支持,而是需先释放写锁再获取读锁;其本质是“写→无锁→读”,存在微小竞态窗口,不保证强一致性,推荐改用StampedLock实现真正降级。

锁降级在 ReentrantReadWriteLock 中并不是一种被显式支持或推荐的机制,它无法真正实现传统意义上的“锁降级”(即先持有写锁再降级为读锁),但可以通过特定方式模拟出类似效果——前提是严格遵循其设计约束。
什么是锁降级?
锁降级指线程在已持有写锁的前提下,不释放写锁而直接获取读锁,之后再释放写锁、仅保留读锁,从而让其他线程能并发读取。这在某些缓存更新场景中很有用:先写入新值(需独占),再允许并发读(需共享)。
注意:ReentrantReadWriteLock 不支持自动锁降级。它的写锁和读锁是互斥的——只要写锁被持有着,任何线程(包括当前线程)都无法成功获取读锁,会阻塞或失败。
为什么不能直接降级?
根本原因在于其内部同步器(AQS)的设计逻辑:
立即学习“Java免费学习笔记(深入)”;
- 写锁是独占锁,要求 state 的高16位为0才允许获取读锁;
- 当写锁被持有时,state 的高16位非零(表示有写锁),此时调用
readLock().lock()会被阻塞; - 即使当前线程已持有写锁,
tryAcquireShared仍会拒绝读锁请求,避免死锁风险和状态混乱。
如何“模拟”锁降级?
唯一安全可行的方式是:先释放写锁,再立即获取读锁。虽然中间存在极短的窗口期(无锁状态),但在正确使用下可保证语义等价:
- 确保写操作已完成且数据已刷新(如 volatile 字段赋值、final 字段初始化);
- 释放写锁前,所有修改对后续读操作可见(JMM 提供 happens-before 保证);
- 紧接着获取读锁,防止其他线程在此间隙修改数据(若业务允许短暂无锁读,则无需读锁)。
示例代码:
rwLock.writeLock().lock();
try {
// 修改共享资源
data = computeNewValue();
// 确保对其他线程可见(如已用 volatile 或 final 保证)
} finally {
rwLock.writeLock().unlock(); // 必须先释放写锁
}
// 此时可安全获取读锁(若还需继续读)
rwLock.readLock().lock();
try {
use(data);
} finally {
rwLock.readLock().unlock();
}
为什么不建议依赖“降级”语义?
因为该模式本质上仍是“写→无锁→读”,存在微小竞态窗口:
- 若其他线程在写锁释放后、读锁获取前抢先获得写锁并修改数据,当前线程后续读到的就是旧值;
- 若业务要求绝对强一致性(写完立刻可读且不可被改),应考虑使用更高级协调机制(如 StampedLock 的乐观读 + validate,或结合版本号/时间戳校验);
-
StampedLock支持真正的锁降级(tryConvertToReadLock),是更合适的选择。


















