readwritelock比synchronized更适合高并发读场景,因其允许多个读线程并发、写时独占,而synchronized读写均串行;但需注意锁配对、禁用锁升级、避免长时读锁阻塞写线程。

ReadWriteLock 为什么比 synchronized 更适合高并发读场景
因为 ReadWriteLock 允许多个线程同时读,但写时独占;而 synchronized 无论读写都串行。当读操作远多于写操作(比如缓存查配置、读用户权限),用 ReentrantReadWriteLock 能显著减少线程阻塞。
但要注意:它不解决“读-写一致性”问题——如果读线程拿到的是写入中途的脏状态,得靠业务逻辑兜底,锁本身不保证可见性顺序。
常见错误现象:NullPointerException 或数据错乱,往往是因为读线程拿到了未完全构造好的对象引用,而非锁没生效。
正确初始化和使用 ReentrantReadWriteLock 的关键点
必须把读锁和写锁分别持有、分别释放,且不能嵌套错位(比如用读锁去 unlock 写锁)。
-
readLock.lock()和writeLock.lock()必须配对unlock(),建议用try/finally - 不要在持有写锁时再去 try 获取读锁(会死锁),
ReentrantReadWriteLock不支持锁升级 - 读锁可重入,但多个线程持同一读锁不会互相阻塞;写锁是排他且不可重入(除非同一线程)
- 默认是非公平策略,若读多写少,可考虑用
new ReentrantReadWriteLock(true)启用公平模式,避免写线程饿死
什么时候不该用 ReadWriteLock
当写操作占比超过 10%~15%,或每次读操作极轻量(比如只读一个 int 字段),ReadWriteLock 的锁开销反而可能高于 synchronized。
典型误用场景:
- 用它保护一个
ConcurrentHashMap——后者本身已做分段/跳表优化,加锁纯属冗余 - 读操作里包含远程调用或 I/O,导致读锁长期占用,把写线程全堵住
- 把整个方法体包在
readLock里,但中间有非线程安全的本地变量修改
性能影响:锁对象本身有内存屏障和 CAS 开销,JVM 对 synchronized 的锁消除、偏向锁等优化在 ReadWriteLock 上不生效。
一个安全的缓存读写示例(Java)
private final Map<string object> cache = new HashMap();
private final ReadWriteLock lock = new ReentrantReadWriteLock();
public Object get(String key) {
lock.readLock().lock();
try {
return cache.get(key); // 仅读,不修改 cache 结构
} finally {
lock.readLock().unlock();
}
}
public void put(String key, Object value) {
lock.writeLock().lock();
try {
cache.put(key, value);
} finally {
lock.writeLock().unlock();
}
}</string>
注意:cache 不能是 ConcurrentHashMap,否则双重同步无意义;也不能是 Collections.synchronizedMap,它内部用的是全局锁,和 ReadWriteLock 冲突。
容易被忽略的一点:如果 get 返回的对象本身可变(比如返回一个 ArrayList),那读锁只保住了“引用读取”的原子性,后续对内容的修改仍需额外同步——锁不传递到对象内部。










