reentrantreadwritelock不支持读锁升级为写锁,因会导致死锁、破坏读共享语义且无法保障可见性;降级则安全可行,需按“获取写锁→修改→获取读锁→释放写锁”顺序执行。

Java 中的读写锁(ReentrantReadWriteLock)**不支持升级**(即不能在持有读锁时获取写锁),但**支持降级**(即在持有写锁时获取读锁,然后释放写锁,从而保留读锁)。这是由锁的设计逻辑决定的:升级会导致死锁风险,而降级是安全且常见的模式。
为什么读锁不能升级为写锁?
如果允许线程在持有读锁的情况下尝试获取写锁,而此时其他线程也持有着读锁,那么该线程会一直阻塞等待所有读锁释放——但其他读线程又可能因该线程占着读锁而无法推进,形成循环等待,引发死锁。JVM 显式禁止这种操作,调用 writeLock().lock() 时会抛出 IllegalMonitorStateException。
如何正确实现锁降级?
降级必须严格按顺序进行:先获取写锁 → 修改数据 → 获取读锁 → 释放写锁。关键在于“获取读锁”和“释放写锁”之间不能有其他线程插入写操作,因此整个过程需在写锁保护下完成。
- 使用同一个
ReentrantReadWriteLock实例,分别调用其readLock()和writeLock() - 必须先成功获取写锁,再在写锁未释放前调用
readLock().lock() - 随后立即释放写锁(
writeLock().unlock()),此时读锁仍被当前线程持有 - 后续可继续使用读锁,直到显式调用
readLock().unlock()
示例代码片段:
ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
Lock readLock = rwLock.readLock();
Lock writeLock = rwLock.writeLock();
// 降级流程
writeLock.lock();
try {
// 修改共享状态
data.update();
// 在写锁保护下获取读锁(允许)
readLock.lock();
} finally {
writeLock.unlock(); // 释放写锁,但读锁仍在
}
// 此时仅持有读锁,其他线程可并发读,但不可写
try {
// 安全读取
data.get();
} finally {
readLock.unlock();
}
有没有替代方案实现“类升级”效果?
若业务逻辑确实需要“先读再决定是否写”,应避免在读锁内尝试升级。推荐做法是:
- 先用读锁快速检查条件(如缓存是否存在、值是否满足更新前提)
- 释放读锁
- 获取写锁,重新验证条件(防止竞态,即 check-then-act 场景中的“失效重检”)
- 确认无误后再执行修改
这种模式称为 **“双重检查锁定”(Double-Check Locking)**,虽不是真正的锁升级,但能安全达成类似目的。
使用注意事项
降级虽可行,但容易误用:
- 降级后若忘记释放读锁,会造成资源泄漏
- 读锁和写锁的 lock/unlock 必须成对,且不能跨线程传递锁所有权
- 锁降级适用于“写后立即读且需长期保持读视图”的场景(如初始化后只读访问)
- 多数场景下,更推荐分离读写路径,而非依赖降级
不复杂但容易忽略细节,实际使用中建议优先考虑无锁设计或更高级的并发结构(如 StampedLock 的乐观读),必要时再谨慎使用降级。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











