readwritelock 的读写锁切换不是自动的,而是需显式释放旧锁再获取新锁;读锁不可升级为写锁,否则死锁;写锁可降级为读锁,但须严格按 writelock→readlock→writelock.unlock() 顺序执行。

ReadWriteLock 的读写锁切换不是“自动切换”,而是由线程显式申请不同锁,并受严格规则约束。核心在于:读锁和写锁是两把独立但状态联动的锁,切换行为本质是“释放旧锁 + 获取新锁”,而非原子升级或降级。
读锁不能升级为写锁
这是最关键的限制。一个已持有读锁的线程,**无法在不释放读锁的前提下获取写锁**。否则会引发死锁风险——它阻塞了其他写线程,又因自身还占着读锁,导致后续所有写操作(包括自己)都无法推进。
- 尝试直接调用
writeLock().lock()会一直阻塞,直到所有读锁被释放(包括它自己的) - 常见错误写法:
readLock.lock(); ... writeLock.lock();→ 必然死锁(除非先 unlock 读锁) - 正确做法:必须先
readLock.unlock(),再writeLock.lock();但此时数据可能已被其他线程修改,需重新校验
写锁可以安全降级为读锁
持有写锁的线程,可以在不释放写锁的情况下获取读锁,然后释放写锁,从而“降级”为仅持读锁。这保证了写后立即读的一致性,且不破坏锁公平性。
- 典型场景:更新缓存后立刻读取最新值,避免写锁释放后被其他线程抢先修改
- 操作顺序必须严格:
writeLock.lock() → readLock.lock() → writeLock.unlock() - 注意:降级后仍需最终调用
readLock.unlock(),否则造成资源泄漏
读写锁互斥与并发规则
切换逻辑始终服从底层状态控制:ReentrantReadWriteLock 使用一个 int 的高16位记读锁计数、低16位记写锁重入次数。任何切换都基于该 state 的原子判断。
- 读锁获取条件:写锁计数为 0(即
state & 0xFFFF == 0) - 写锁获取条件:读锁计数为 0 且写锁未被其他线程持有(即
state >>> 16 == 0且当前无写锁 owner) - 因此,“切换”本质是两次独立的 acquire 操作,中间存在窗口期——即使降级也需确保写锁 owner 是当前线程
实际切换建议与模式
不要依赖“无缝切换”,而应按业务语义设计锁生命周期。常见可靠模式有:
- 读-改-写模式:先读 → 发现需更新 → 放弃读锁 → 获取写锁 → 修改 → 释放写锁(不保留读锁)
- 写后读模式(降级):获取写锁 → 修改 → 获取读锁 → 释放写锁 → 使用数据 → 释放读锁
- 双重检查锁定(Double-Check):读锁内检查状态 → 不满足则释放读锁 → 获取写锁 → 再次检查 → 更新 → 降级或释放
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











