读写锁通过分离读写权限实现读并发、写互斥,在读多写少场景提升吞吐量;其核心是状态协同而非简单双锁,需严格遵循读锁只读、写锁才写的契约,并依语义分级保护共享变量。

读写锁模式的核心是把对共享数据的访问权限拆成“读”和“写”两类,让读操作能并发执行,写操作则严格互斥。这种分离不是简单加两把锁,而是通过状态协同实现读不阻塞读、读写互斥、写写互斥,从而在读多写少场景下显著提升吞吐量。
读写锁的基本行为逻辑
ReentrantReadWriteLock 内部维护一个同步状态,用位域分别记录读锁持有数与写锁持有线程信息:
- 读锁可重入、共享:同一或不同线程可同时获取读锁,前提是当前无写锁被持有
- 写锁独占、可重入:获取写锁时必须确保既无其他写锁,也无任何读锁活跃
- 读锁不能升级为写锁:否则会引发死锁(因已持读锁,再等写锁需先释放读锁,但释放后状态可能被篡改)
- 写锁可降级为读锁:先持写锁修改数据,再获取读锁,最后释放写锁,保持数据可见性且允许后续并发读
变量分离的关键实践:避免读锁内写、写锁外读
读写锁生效的前提是严格遵守“读锁只读、写锁才写”的契约。常见误用包括:
- 在 readLock.lock() 后调用 map.put() 或修改对象内部状态——这破坏锁语义,可能导致数据不一致
- 在 writeLock.lock() 中调用远程接口、阻塞 I/O 或回调监听器,延长写锁持有时间,加剧读线程排队
- 将读写共用的中间变量(如临时计算结果、缓存校验标记)放在锁外更新,导致读线程看到陈旧或部分更新的值
正确做法是:所有影响共享数据一致性的读写操作,必须包裹在对应锁内;非共享的局部变量或纯计算无需加锁。
典型场景中的变量隔离设计
以本地缓存为例,真正需要保护的是底层存储结构(如 HashMap),而元信息(如命中率计数器、最后刷新时间)是否加锁取决于一致性要求:
- 命中率统计(counter++)建议用原子类(AtomicLong),避免为统计争抢读锁
- 最后刷新时间(lastUpdate)若用于读路径的过期判断,则应在读锁内读取、写锁内更新,保证可见性
- 缓存重建触发条件(如后台刷新开关)可用 volatile boolean,无需锁,但需配合 happens-before 保障
这种按语义分级保护的方式,比统一用排他锁更精细,也更贴近真实并发压力分布。
性能边界与模式选择提醒
读写锁不是银弹。当读写比例接近 1:1 或写操作频繁时,其开销(状态检查、队列管理、上下文切换)可能反超普通 ReentrantLock。实测表明,读写比大于 8:1 时优势明显;低于 3:1 时需结合压测评估。
- 默认构造的 ReentrantReadWriteLock 是“读优先”,适合大多数缓存、配置中心场景
- 若写操作延迟敏感(如实时风控规则更新),可启用公平模式 new ReentrantReadWriteLock(true),按请求顺序调度,防写饥饿
- 对极致性能有要求且读写逻辑完全解耦,可考虑 StampedLock 的乐观读机制,但需处理验证失败重试逻辑

















