ReadWriteLock的读锁仅在存在写锁、写锁排队(公平模式)或锁降级未释放写锁时被阻塞;读锁不可升级为写锁,否则死锁;应通过轻量读操作、写合并、绕过锁及监控优化并发性能。

ReadWriteLock 的读锁本身不阻塞其他读锁,但会在特定条件下被阻塞——关键不是“读锁是否阻塞”,而是“读锁何时拿不到”。真正影响读操作并发性的,是写锁的存在和获取时机。
读锁被阻塞的三种典型场景
读锁看似“共享”,但它的获取并非无条件。以下情况会导致线程在调用 readLock.lock() 时被挂起(阻塞):
- 有线程正持有写锁:这是最常见原因。只要写锁被占用(哪怕只有一毫秒),所有新请求读锁的线程都会排队等待写锁释放。
-
有写锁正在排队等待(且策略为公平锁):在公平模式下(
new ReentrantReadWriteLock(true)),如果已有写线程在队列中等待,后续读线程即使先发起请求,也会让位于等待中的写线程,从而被阻塞。 - 锁降级后未及时释放写锁:比如某线程先获取写锁、再获取读锁、最后只释放写锁——此时它仍持读锁,但若其他线程在此期间尝试获取写锁,该写锁请求会阻塞后续所有读锁申请(因为写锁请求优先级高于新读请求)。
读锁不阻塞读锁,但可能“饿死”写锁
读锁的高并发性是一把双刃剑。当大量读线程持续涌入,而写操作又相对频繁时,写线程可能长期得不到执行机会——即发生“写饥饿”。这不是读锁主动阻塞写锁,而是写锁在竞争中始终排在队尾。
- 非公平模式(默认)下,新读线程可能不断插队,加剧写饥饿;
- 公平模式虽能缓解,但会牺牲整体吞吐量,读操作响应延迟上升;
- 实践中,若写操作有强时效性(如实时更新配置),需配合超时机制或独立写线程池隔离。
避免误用:读锁不能升级为写锁
一个常见误区是试图在持有读锁时直接获取写锁(即“锁升级”)。这会导致死锁风险,JDK 明确禁止:
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
- 线程 A 持有读锁 → 尝试 lock writeLock → 阻塞(因自己占着读锁,写锁要求无任何读锁);
- 此时其他线程也无法获取写锁或新读锁(写锁排队会阻塞后续读);
- 结果:A 卡住,整个读写链路停滞。
正确做法是:先 unlock() 读锁 → 再 lock() 写锁 → 完成写入 → 如需继续读,可重新获取读锁(即“锁降级”是唯一安全路径)。
实用建议:如何减少读锁阻塞感知
阻塞本身无法完全消除,但可通过设计降低其影响:
- 读操作尽量轻量:避免在 readLock 临界区内做耗时操作(如远程调用、复杂计算),缩短持锁时间;
- 写操作预判与合并:对高频小更新,考虑批量写入或使用 CAS + volatile 替代锁;
- 区分读场景:对一致性要求不高的读取(如监控指标),可绕过读锁,直接读 volatile 字段;
-
监控锁状态:利用
rwl.getReadLockCount()和rwl.isWriteLocked()做运行时诊断,定位长时写锁或读锁堆积点。
不复杂但容易忽略:读锁的“不阻塞”是有前提的,理解这个前提比记住语法更重要。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










