reentrantreadwritelock默认非公平模式下不保证绝对优先级,而是依据锁状态和等待队列动态调度:写锁未占用且无等待写线程时读线程可立即获取;一旦有写线程入队,后续读线程即被阻塞,从而在读多写少场景易引发写饥饿。

ReentrantReadWriteLock 并不保证读写操作的绝对优先级,它默认采用“写优先”策略,但实际行为取决于锁的获取顺序和当前持有状态,而非严格的优先级调度。
读锁与写锁的竞争规则
当多个线程同时尝试获取锁时,ReentrantReadWriteLock 遵循以下核心逻辑:
- 如果写锁未被占用,且等待队列中没有写线程,新来的读线程可直接获取读锁(即使已有其他读锁)
- 一旦有线程在等待写锁(即写线程已调用 lock() 但尚未获取成功),后续的读线程将被阻塞,即使此时读锁是空闲的
- 写锁始终排斥所有读锁和其它写锁;读锁之间可重入共享,但排斥写锁
- 公平模式下(构造时传入 true),按 FIFO 顺序排队,写线程不会被大量读线程“饿死”;非公平模式(默认)允许插队,可能加剧写饥饿
写饥饿与读饥饿的实际表现
默认非公平模式下,持续高频的读请求可能导致写线程长期无法获取锁(写饥饿);而一旦写锁被持有一段时间,又会批量阻塞所有新读请求(读饥饿)。这不是设计缺陷,而是权衡吞吐与公平的结果。
- 写饥饿常见于:读多写少场景 + 非公平模式 + 写操作耗时较长
- 读饥饿发生在:写线程持有锁期间,新读线程必须等待写锁释放后才能尝试获取(即使写锁释放后立刻有读线程排队)
- 注意:已获取读锁的线程不受影响,它们可继续执行,直到主动释放;阻塞只发生在“申请阶段”
如何控制竞争倾向
不能通过 API 设置“读优先”或“写优先”开关,但可通过配置和使用方式间接引导行为:
- 启用公平模式:new ReentrantReadWriteLock(true),让等待时间最长的线程优先获得锁,缓解写饥饿
- 避免在读锁内做耗时操作(如 I/O、远程调用),减少读锁持有时间,降低对写线程的压制
- 写操作尽量轻量,或拆分为“先计算后更新”,缩短写锁临界区
- 必要时考虑降级为普通 ReentrantLock,或使用更高级的并发结构(如 StampedLock 的乐观读)
一个典型竞争时序示例
假设线程 A(读)、B(读)、C(写)、D(读)依次申请锁(非公平模式):
- A、B 先成功获取读锁 → 读锁计数为 2
- C 请求写锁 → 被阻塞(因读锁正被持有)
- D 请求读锁 → 也被阻塞(因队列头部已有写线程 C 在等待)
- A、B 释放读锁 → 写锁立即授予 C,D 必须等 C 释放写锁后才可能获取读锁
这个过程体现了“写线程一旦入队,就阻挡后续读线程”的关键机制。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











