公平锁性能折损核心在于hasQueuedPredecessors()方法:每次加锁前必须严格检查队列中是否存在更早排队线程,涉及volatile读、缓存行加载、多次判断与线程比较,强制增加物理开销;而非公平锁直接CAS抢锁,仅失败时才排队,因而吞吐量更高。

ReentrantLock 的公平锁性能折损,核心就落在 hasQueuedPredecessors() 这个方法上。它不是简单的“队列是否为空”判断,而是每次尝试加锁前,都必须严格检查:当前线程是否真的是队列里等待最久的那个——也就是确保没有比它更早排队的线程存在。
为什么这个检查会带来开销
该方法的逻辑是:若同步队列中存在一个非头结点的等待节点(即 head.next != null 且其 thread 字段不为 null),并且该节点的线程不是当前线程,则返回 true,表示有前驱,本次获取锁失败。
这意味着每次 lock() 调用都要:
- 读取 volatile 修饰的 head 和 tail 引用(内存屏障开销)
- 访问 head.next 节点并读取其 thread 字段(可能触发缓存行加载)
- 做两次非空判断 + 一次线程对象比较
- 即使队列为空或只有自己,也必须走完这套流程才能放行
和非公平锁的关键差异点
非公平锁在 lock() 开头直接执行 CAS 尝试抢锁(compareAndSetState(0, 1)),成功就拿走,完全绕过队列检查;失败才退到 acquire 流程,走 AQS 标准排队逻辑。
而公平锁的 tryAcquire() 实现强制前置调用 hasQueuedPredecessors(),哪怕此时锁刚好释放、state=0,也必须先确认“无人插队”,再执行 CAS。这多出的一次队列探查,就是吞吐量下降的直接原因。
实际影响有多大
在高竞争场景下(如百线程争一把锁),公平锁的平均加锁延迟通常比非公平锁高出 15%–40%,具体取决于 CPU 缓存一致性协议效率和队列长度。JDK 文档明确指出:“公平锁一般会导致较低的总体吞吐量”。这不是实现缺陷,而是顺序保证所付出的必然代价。
什么情况下值得接受这个折损
仅当业务对响应时间的可预测性要求极高,且能容忍吞吐下降时才启用。例如:
- 实时计费系统,要求请求处理延迟抖动极小
- 调试或压测中需复现确定性线程调度行为
- 资源分配策略依赖严格的 FIFO 语义(如任务队列分发)
日常业务中,绝大多数场景应坚持使用默认的非公平模式。











