
公平锁在高争用场景下吞吐量明显下降
公平锁(ReentrantLock(true))强制线程按入队顺序获取锁,避免饥饿但引入额外调度开销。压测中常见现象是:QPS 比非公平模式低 30%–60%,尤其在线程数 > CPU 核心数、临界区短(70% 的场景下更明显。
- 公平性依赖
CLH queue维护等待节点,每次 acquire 都需 CAS 更新 tail,争用激烈时失败重试增多 - 线程唤醒后不能立即执行,必须等前序所有排队线程都轮到——导致 CPU 利用率下降、上下文切换增加
- 非公平锁(默认)允许新线程“插队”尝试 CAS 获取锁,成功则跳过排队,对缓存行友好,也更贴合真实负载的突发性
压测时如何验证公平性带来的性能差异
别只看平均延迟,重点观察 P99 延迟毛刺和线程阻塞率。用 JFR 或 jstack 抓样,搜索 parking to wait for 可快速定位是否大量线程卡在 AQS 队列里。
- 对比实验必须固定线程数、总请求数、JVM 参数(尤其是
-XX:+UseParallelGC或-XX:+UseZGC要一致),否则 GC 干扰会掩盖锁行为 - 用
JMH写微基准测试时,确保@Fork(jvmArgsAppend = "-XX:-OmitStackTraceInFastThrow"),避免异常优化干扰锁路径 - 生产级压测建议用
arthas thread -n 10查看 top 10 阻塞线程堆栈,确认是否集中阻塞在AbstractQueuedSynchronizer.acquire
公平锁不是“更安全”,它只是换了一种不安全的方式
很多人误以为公平锁能防止死锁或提升一致性,其实它既不改变原子性,也不影响可见性。它只调整调度策略,反而可能暴露原本被非公平性掩盖的问题。
- 比如多个锁嵌套时,公平模式更容易触发循环等待(因严格 FIFO,线程推进节奏更同步)
- 若业务逻辑本身存在“先锁 A 再锁 B”的隐式顺序,公平锁会让线程更大概率卡在同一个位置,放大锁顺序问题
-
tryLock(long, TimeUnit)在公平模式下返回 false 的概率更高——不是因为锁不可用,而是因为队列太长,超时前根本轮不到
什么情况下真该用公平锁
只有两类场景值得考虑:确定性响应时间要求(如实时系统),或调试/复现偶发竞争问题。除此之外,默认用非公平锁。
立即学习“Java免费学习笔记(深入)”;
- 实时系统需 P99 延迟稳定在 5ms 内,且能接受吞吐量折损——此时公平性带来可预测性,比绝对吞吐更重要
- 用公平锁复现
ConcurrentModificationException或数据不一致 bug,因为它的执行顺序更可控、更容易重现 - 注意:
Synchronized永远是非公平的,且 JVM 8u60+ 后已无公平开关;不要试图通过它模拟公平行为









