reentrantreadwritelock支持写锁降级为读锁,需严格按三步执行:先获取写锁、再获取读锁、最后释放写锁;禁止读锁升级,否则引发死锁与可见性问题。

写锁降级为读锁的三步必须顺序执行
锁降级不是自动转换,而是线程主动、严格按序完成的显式操作。它只允许“写→读”单向降级,且三步缺一不可:
-
第一步:获取写锁——调用
writeLock.lock(),确保当前线程独占临界区,可安全修改共享数据; -
第二步:在未释放写锁前获取读锁——立即调用
readLock.lock();此时因同一线程已持写锁,读锁重入成功,不会阻塞; -
第三步:释放写锁——调用
writeLock.unlock();释放瞬间触发内存屏障,使之前所有写操作对其他线程可见,而本线程仍持有读锁,继续受保护。
这三步构成原子性同步契约:写锁释放(happens-before)后续其他线程成功获取读锁的动作。跳过第二步、颠倒二三步、或在中间插入写操作,都会导致其他线程看到过期值或中间态。
为什么读锁不能升级为写锁
ReentrantReadWriteLock 明确禁止读锁升级,这不是实现限制,而是为规避根本性并发风险而做的强制设计:
- 死锁必然发生:若线程 A、B 均持有读锁,又同时尝试获取写锁,则双方都在等对方释放读锁——形成循环等待;
- 破坏读锁的共享语义:读锁本意是允许多线程并发读;一旦允许升级,就等于让某个读线程“悄悄抢占写权”,其他仍在读的线程无法感知变更,导致可见性断裂;
- 升级后无法保证原子性:即使某线程侥幸升级成功并写入,它释放读锁前,其他线程仍可能拿着旧读锁继续读,看到的仍是降级前的数据。
所以,任何“先读再决定是否写”的逻辑,必须放弃升级幻想——直接用写锁兜底:不拿读锁,一开始就上写锁做判断和修改。
降级后读锁仍需手动释放
完成降级不等于操作结束。线程此时仅持有读锁,后续只读访问虽可并发,但该读锁必须由同一线程显式释放:
- 忘记调用
readLock.unlock()会导致读锁泄漏,后续所有写锁请求被永久阻塞; - 在
finally块中配对释放,是唯一稳妥做法; - 多次降级(如嵌套场景)需注意读锁可重入计数,unlock 次数须与 lock 次数一致。
降级的价值在于平滑过渡:写完不急着放行写权,而是以读锁延续保护,既防止写后读被干扰,又开放并发读能力。










