
ReadWriteLock 为什么不能直接 new?
Java 里没有 ReadWriteLock 的具体实现类可直接 new,它只是个接口。你真正要用的是 ReentrantReadWriteLock —— 这是 JDK 提供的唯一标准实现,也是绝大多数场景下的唯一选择。
常见错误是写成:new ReadWriteLock(),编译直接报错;或者误以为 StampedLock 是它的子类(其实不是,它是独立设计,API 和语义都不同)。
- 必须用
new ReentrantReadWriteLock()实例化 - 构造支持传
boolean参数:设为true启用公平模式(写线程优先获取锁),但会显著降低吞吐量,读多写少时通常保持默认false - 公平模式下,即使只有读线程在等,新来的写线程也会插队,反而让读饥饿——这和直觉相反,得小心
读锁和写锁必须配对 unlock(),且不能跨线程
ReentrantReadWriteLock 的读锁和写锁都支持重入,但释放必须由**同一线程**完成,且 lock() 和 unlock() 必须严格配对。漏掉 unlock() 不会立刻报错,而是导致其他线程永久阻塞。
典型翻车现场:在 try 里 lock(),但把 unlock() 写在 finally 外面;或用 return 提前退出,跳过了释放逻辑。
立即学习“Java免费学习笔记(深入)”;
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 读锁和写锁的
unlock()不能混用:拿读锁必须用readLock().unlock(),写锁同理 - 不要在 lambda 或异步回调里释放锁——线程变了,
unlock()会抛IllegalMonitorStateException - 建议用 try-with-resources 包一层(需自行封装
AutoCloseable),比裸写finally更可靠
读锁不互斥,但会阻塞写锁 —— 注意“写饥饿”风险
多个读线程可以同时持有读锁,这是提升并发读性能的关键。但只要有一个读锁没释放,所有写线程就卡在 writeLock().lock() 上。如果读操作频繁、耗时长(比如读大对象、IO 等待),写线程可能一直等不到机会。
这不是 bug,是设计使然。但业务上常被忽略:比如缓存刷新(写)总被查缓存(读)压着,导致数据长期不更新。
- 避免在读锁内做耗时操作,尤其是网络调用、数据库查询、大对象拷贝
- 考虑降级策略:给写锁加超时(
writeLock().tryLock(1, TimeUnit.SECONDS)),失败则走补偿流程 - 如果写操作也频繁,说明已不满足“读多写少”前提,
ReadWriteLock反而比普通synchronized更慢(锁开销更大)
和 synchronized 比,什么情况下反而更慢?
ReentrantReadWriteLock 的锁管理比 synchronized 重得多:要维护读写状态计数、线程队列、唤醒逻辑。当读操作本身极轻(比如只读几个字段)、且竞争极少时,它带来的额外开销可能超过收益。
简单测过:单线程反复读一个 int 字段,用 ReentrantReadWriteLock 比 synchronized 慢 2–3 倍;但 10 个线程并发读,差距就反转了。
- 别为了“看起来高级”而用——先确认真实瓶颈在读竞争,而不是 CPU 或 GC
- 注意 JVM 对
synchronized的优化(偏向锁、轻量级锁),在低争用下非常高效 - 如果读写比例接近 1:1,或写操作占比超 20%,基本不用考虑它
真正难的不是怎么写那几行 lock() 和 unlock(),而是判断此刻要不要用它、以及用的时候有没有悄悄把读操作拖慢到让写线程饿死。


















