要真正切开高频读与低频写的互斥链条,关键在于严格遵循读写锁行为边界:读锁间不互斥、读写/写写互斥、写锁可降级、读锁不可升级;明确读写边界,避免混用锁;用写锁降级替代先读后写;控制读锁粒度防写饥饿;依场景选择公平或非公平策略。

要真正把高频读和低频写的互斥链条“切开”,关键不是简单套用读写锁,而是理解并落实它的行为边界——读锁之间不互斥、读与写互斥、写与写互斥,且写锁能降级、读锁不能升级。只有按这个逻辑组织代码,才能让读操作彻底摆脱写操作的阻塞链。
明确读写边界,避免混用锁
读操作必须只持读锁,写操作必须只持写锁,中间不能穿插未加锁的共享状态访问。常见错误是:在读锁保护下调用了一个可能触发写逻辑的方法,或在写锁释放前又去尝试获取读锁(造成死锁风险)。
- 读方法里只做查询、计算、返回值,不修改任何共享字段
- 写方法里完成所有变更,包括校验、赋值、清理缓存等,不对外暴露未同步的中间态
- 避免在持有读锁时调用外部回调或监听器——它们可能间接写入共享数据
用写锁降级替代“先读后写”逻辑
当业务需要“读取当前值 → 判断是否需更新 → 写入新值”时,直接用读锁+写锁会引发两次锁竞争,还可能因读写间隙导致脏读。正确做法是:先获取写锁,再在写锁内完成读判写,最后选择性降级为读锁(如果后续还需读取结果)。
- 写锁获取后,可安全读取最新状态,无需额外同步
- 更新完成后,若需返回新值或供其他线程立即读取,可紧接着获取读锁,再释放写锁(即“降级”)
- 注意:降级必须在同一 thread 内完成,且不能在持有读锁时再去申请写锁
控制读锁粒度,防止长读阻塞写入
读锁虽允许多线程并发,但若某个读操作耗时过长(如遍历大集合、IO等待、复杂计算),它会一直占用读锁,导致写锁请求被挂起,引发写饥饿。这不是锁设计缺陷,而是使用失当。
- 读锁内只做轻量级内存访问,耗时操作移出锁作用域
- 对大数据结构,考虑复制快照(如用 CopyOnWriteArrayList)或分段读取
- 必要时设置读操作超时,或用 tryLock 避免无限等待
配合锁策略缓解写优先带来的读延迟
ReentrantReadWriteLock 默认偏向写线程(写锁可插队),这保障了写不饿死,但也可能导致持续读流量下写请求迟迟得不到执行。若业务允许一定延迟,可通过构造函数启用公平模式:
- new ReentrantReadWriteLock(true) 启用公平策略,使锁按 FIFO 分配
- 公平模式下读吞吐略降,但写响应更可预期,适合配置类、元数据等对写及时性敏感的场景
- 非公平模式(默认)更适合纯缓存类场景,读性能优先











