ReadWriteLock通过读写分离提升读多写少场景吞吐量,允许多个读线程并发、写操作独占,典型实现为ReentrantReadWriteLock;需按需加锁、及时释放,禁止读锁升级写锁,支持写锁降级与重入。

在读多写少的并发场景中,ReadWriteLock 能显著提升吞吐量,因为它允许多个读线程同时访问共享资源,而写操作则独占锁——这比直接用 synchronized 或 ReentrantLock(全互斥)更高效。
理解 ReadWriteLock 的核心分工
ReadWriteLock 不是锁本身,而是一个接口,典型实现是 ReentrantReadWriteLock。它拆分为两个独立的锁:
- 读锁(shared lock):可被多个线程同时持有,前提是当前没有线程持有写锁;
- 写锁(exclusive lock):是排他锁,获取时要求既无其他写锁,也无任何读锁在持有中。
这种“读读不互斥、读写/写写互斥”的策略,天然适配缓存、配置、只读元数据等读远多于写的场景。
正确使用 readLock 和 writeLock
关键不是“加锁”,而是“按需加锁 + 及时释放”,尤其注意写锁必须保护所有可能修改共享状态的操作,读锁要包裹所有依赖该状态的读取逻辑:
立即学习“Java免费学习笔记(深入)”;
- 读操作必须用
readLock().lock()获取读锁,并在finally中调用unlock(); - 写操作必须用
writeLock().lock()获取写锁,同样在finally中释放; - 不要在持有读锁时尝试升级为写锁(会死锁),如需“先读后写”,应先释放读锁再申请写锁;
- 避免在锁内执行耗时操作(如 I/O、远程调用),否则会阻塞其他线程。
示例:安全的带缓存的计数器
private final Map<String, Integer> cache = new HashMap<>();
private final ReadWriteLock lock = new ReentrantReadWriteLock();
public int getCount(String key) {
lock.readLock().lock();
try {
return cache.getOrDefault(key, 0);
} finally {
lock.readLock().unlock();
}
}
public void increment(String key) {
lock.writeLock().lock();
try {
cache.merge(key, 1, Integer::sum);
} finally {
lock.writeLock().unlock();
}
}
注意公平性与重入行为
ReentrantReadWriteLock 默认是非公平的(写锁可能插队),适合高吞吐;若需写操作不被大量读操作“饿死”,可启用公平模式:new ReentrantReadWriteLock(true)。
它支持重入:同一线程可多次获取自己已持有的读锁或写锁,但每次 lock() 都需对应一次 unlock()。写锁可降级为读锁(即持有写锁后,再获取读锁,然后释放写锁),但读锁不能升级为写锁。
和 synchronized 对比的实际收益
在纯读场景下,synchronized 方法同一时刻只允许 1 个线程执行,而 readLock 可支持几十甚至上百线程并发读;只要写操作占比低于约 10%–15%,整体吞吐量通常能提升 2–5 倍(取决于线程数和临界区大小)。但要注意:锁粒度仍需合理——不要把整个对象包装成一个大读写锁,应按数据访问模式拆分锁边界。


















