ReentrantLock默认是非公平锁,启用公平锁需显式传true;公平锁按FIFO排队但吞吐低、延迟高,非公平锁允许插队且性能更好,适用场景需权衡。

ReentrantLock 构造时怎么选公平模式
默认是非公平锁,要启用公平锁必须显式传 true 给构造函数。不传或传 false 都是非公平——这点容易被误以为“不传就是公平”。
-
new ReentrantLock()→ 非公平(等价于new ReentrantLock(false)) -
new ReentrantLock(true)→ 公平,线程按等待顺序获取锁
公平模式下,新来的线程不会插队,但会带来额外开销:每次加锁都要检查同步队列是否为空、是否有前驱节点,还要维护 FIFO 队列结构。
公平锁在高竞争下反而可能降低吞吐量
非公平锁允许刚释放锁的线程立刻重入(即“唤醒后直接抢”),减少了上下文切换和线程阻塞;而公平锁强制排队,即使持有锁的线程刚释放,下一个线程也得等前面所有等待者轮完。
- 低竞争场景(如锁很少争用):公平/非公平差异不明显
- 高竞争场景(如多线程频繁进出临界区):非公平锁通常吞吐更高,延迟更稳定
- 公平锁适合对响应时间敏感且需可预测调度的场景(如实时任务、调试环境)
实测中,开启公平锁后,在 20+ 线程争用同一把锁时,平均获取延迟可能上升 3–5 倍,吞吐下降约 30%~40%。
如何验证当前锁是否启用公平模式
没有公开 API 直接返回“是否公平”,但可通过反射读取 sync 字段的实现类名判断:
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
Field syncField = ReentrantLock.class.getDeclaredField("sync");
syncField.setAccessible(true);
Object sync = syncField.get(lock);
boolean isFair = sync.getClass().getName().contains("FairSync");
不过这属于内部实现细节,不同 JDK 版本类名可能变化(如 FairSync / NonfairSync),生产环境不建议依赖。更稳妥的方式是:自己封装一个带标记的子类,或在创建时记录配置。
tryLock(long, TimeUnit) 在公平锁下仍可能失败
即使启用了公平模式,tryLock(100, TimeUnit.MILLISECONDS) 也不是“保证最多等 100ms 后一定拿到锁”,而是“最多阻塞 100ms,期间若未获得锁就返回 false”。公平性只影响排队顺序,不影响超时逻辑本身。
- 公平锁下,该线程仍需排在队列尾部,前面有 5 个等待者?那它大概率超时失败
- 非公平锁下,它还有机会在锁释放瞬间“插队成功”,成功率略高
- 无论哪种模式,
tryLock()(无参)都**不遵守公平性**,永远是非公平尝试
真正需要强公平保障的业务逻辑,不能只靠 tryLock + 超时,得配合循环重试或降级策略,否则容易在压力下大量失败。
公平锁的语义边界比直觉中窄:它只约束「阻塞后入队再唤醒」的过程,不改变锁释放时机、不消除竞争、也不保证任何线程一定能拿到锁。写并发逻辑时,先想清楚“到底需不需要绝对顺序”,而不是默认选 true。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










