锁降级是线程在仍持有写锁时先获取读锁再释放写锁,必须严格按获取写锁、获取读锁、释放写锁三步执行,以消除竞态窗口并保证写后读的数据一致性。

锁降级不是“把写锁变成读锁”,而是线程在**仍持有写锁时,先获取读锁,再释放写锁**,从而无缝过渡到仅持读锁的状态。它的安全实现不靠魔法方法,而靠三步严格时序和底层状态设计保障。
必须按顺序执行的三个关键动作
这三步缺一不可,顺序错一步,就失去一致性:
-
第一步:获取写锁——调用
writeLock.lock(),独占临界区,完成所有修改逻辑(如更新缓存、刷新状态) -
第二步:在写锁未释放前获取读锁——紧接调用
readLock.lock();此时因当前线程已持写锁,该操作立即成功(属于重入,不阻塞) -
第三步:释放写锁——调用
writeLock.unlock();这一步触发内存屏障,使之前所有写操作对其他线程可见,且线程仍持有读锁
为什么这个顺序能避免竞态
核心是消除“防御真空期”:
- 如果先
writeLock.unlock()再readLock.lock(),中间哪怕只隔几纳秒,其他线程就可能抢到写锁并改数据 - 本线程后续读到的就不是自己刚写的值,而是别人改过的新值或中间态,彻底破坏一致性
- 而正确顺序下,写锁释放与读锁持有之间没有时间窗口——读锁已在写锁释放前就位,临界区从“独占写”平滑过渡到“共享读”
底层支撑:AQS 状态设计允许这种重入
ReentrantReadWriteLock 基于 AQS 实现,其 32 位 state 字段被拆分为两部分:
- 高 16 位:记录读锁持有线程数(shared count)
- 低 16 位:记录写锁重入次数(exclusive count)
当线程已持写锁(exclusive count > 0)时,readLock.lock() 的 tryAcquireShared() 会检查:当前写锁是否由本线程持有。是,则允许获取读锁——这是设计内建的支持,不是临时绕过规则。
降级后读锁必须独立管理
降级完成后,线程只持有读锁,它和原来的写锁已无关联:
- 读锁必须显式调用
readLock.unlock()释放,否则会永久泄漏,阻塞所有后续写操作 - 读锁不可升级为写锁;若已持读锁,再调
writeLock.lock()会永远阻塞(防死锁设计) - 降级不解决单线程内变量可见性(那是 JVM 保证的),只为跨线程提供“写完立刻读”的同步契约
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











