锁降级是线程在持有写锁时先获取读锁再释放写锁,以避免写后读的竞态窗口;必须严格按获取写锁、获取读锁、释放写锁三步执行,否则破坏一致性。

锁降级不是“把写锁变成读锁”,而是线程在**仍持有写锁时,先获取读锁,再释放写锁**,从而无缝过渡到仅持读锁的状态。这个过程的关键不在“转换”,而在**避免写完到开始读之间的竞态窗口**。
必须严格按三步执行
顺序错一步,就失去一致性保障:
-
第一步:获取写锁 —— 调用
writeLock.lock(),独占临界区,完成所有修改逻辑 -
第二步:在写锁未释放前获取读锁 —— 紧接着调用
readLock.lock();此时因当前线程已持写锁,该操作立即成功(属于重入) -
第三步:释放写锁 —— 调用
writeLock.unlock();这一步触发内存屏障,使之前写操作对其他线程可见,且线程仍持有读锁
写锁不能提前释放
写锁一旦释放,其他线程就可能抢入并修改数据——哪怕只延迟几纳秒,也会让本线程后续的读变成“读别人改过的值”。常见错误是:
- 先
writeLock.unlock(),再readLock.lock()→ 中间出现“防御真空期” - 忘记在
try块内调用readLock.lock()→ 写完直接退成无锁状态 - 在
finally中重复释放写锁 → 抛IllegalMonitorStateException
降级后读锁需独立管理
降级完成后,线程只持有读锁,它和原来的写锁已无关联:
- 读锁必须显式调用
readLock.unlock()释放,否则会永久泄漏,阻塞所有后续写操作 - 读锁不可升级为写锁;若已持读锁,再调
writeLock.lock()会永远阻塞 - 降级不解决单线程内变量可见性问题(那是 JVM 保证的),它只为跨线程提供“写完立刻读”的同步契约
典型安全写法模板
实际编码中应这样组织:
writeLock.lock();
try {
updateData(); // 写操作
readLock.lock(); // 关键:降级入口,在写锁还在时拿读锁
} finally {
writeLock.unlock(); // 关键:释放写锁,但读锁仍在
}
try {
useData(); // 安全的只读操作
} finally {
readLock.unlock(); // 必须释放读锁
}Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











