readwritelock通过分离读锁与写锁提升高并发读多写少场景性能,允许多线程并发读、写操作独占,且支持锁降级与公平策略选择。

在高并发场景下,数据查询接口常因共享资源的读写竞争导致性能瓶颈。使用 ReadWriteLock 能显著提升读多写少场景的吞吐量——它允许多个线程同时读,但写操作独占,避免了 synchronized 或 ReentrantLock 的“读-读互斥”浪费。
理解 ReadWriteLock 的核心优势
ReadWriteLock 将锁分为读锁(shared)和写锁(exclusive),遵循以下原则:
- 读锁可被多个线程同时持有,只要没有线程持有写锁;
- 写锁是排他性的,获取时必须确保无其他读锁或写锁活跃;
- 写锁可降级为读锁(需先释放写锁再获取读锁),但读锁不能升级为写锁(会死锁);
- 默认非公平,但
ReentrantReadWriteLock支持构造时指定公平策略(公平模式会增加上下文切换开销,通常非公平更高效)。
典型查询接口中的正确用法
假设有一个缓存用户信息的查询接口,底层是线程不安全的 HashMap,且更新频率远低于查询频率:
private final Map<long user> cache = new HashMap();
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock readLock = lock.readLock();
private final Lock writeLock = lock.writeLock();
public User getUser(long userId) {
readLock.lock();
try {
return cache.get(userId); // 快速读取,无需阻塞其他读线程
} finally {
readLock.unlock();
}
}
public void updateUser(User user) {
writeLock.lock();
try {
cache.put(user.getId(), user);
// 可选:触发缓存一致性处理(如清除关联缓存)
} finally {
writeLock.unlock();
}
}</long>
⚠️ 注意:读操作中禁止执行耗时逻辑(如远程调用、数据库查询)——否则会长时间占用读锁,拖慢整体响应;应把真正“读共享状态”的动作限制在锁内,其余逻辑移出。
避免常见陷阱
- 不要在 synchronized 块内嵌套使用 ReadWriteLock:混合锁机制易引发死锁或语义混乱;统一使用一种锁策略;
- 读锁未在 finally 中释放:会导致后续所有读写操作永久阻塞,务必配对使用 try-finally;
-
误将读操作放在写锁下:例如在
updateUser中顺手调用getUser,不仅降低并发度,还可能造成锁顺序问题; -
忽略锁的可重入性边界:虽然
ReentrantReadWriteLock支持重入,但同一线程反复获取读锁仍需对应次数的 unlock,否则影响其他线程; - 用错锁粒度:若缓存分片(如按 userId 取模),可为每个分片配独立锁,进一步减少争用,而非全局一把读写锁。
配合 volatile + CAS 做轻量兜底(进阶)
对于纯只读、极少更新的配置类数据,可结合 volatile 引用 + ReadWriteLock 实现“无锁读 + 安全写”:
private volatile Config currentConfig;
private final ReadWriteLock configLock = new ReentrantReadWriteLock();
// 读:完全无锁,靠 volatile 保证可见性
public Config getConfig() {
return currentConfig;
}
// 写:加写锁确保更新原子性与可见性传播
public void updateConfig(Config newConfig) {
writeLock.lock();
try {
currentConfig = newConfig; // volatile 写,对所有线程立即可见
} finally {
writeLock.unlock();
}
}
这种组合在配置热更新等场景下,能实现 99% 查询零锁开销,仅更新时短暂阻塞。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











