公平锁通过hasqueuedpredecessors()前置检查确保fifo顺序,新线程必须排到队尾且仅当无前驱时才尝试获取锁,虽避免饥饿但降低吞吐量。

公平锁的等待队列严格按 FIFO 排序
在 ReentrantLock 的公平模式下,AQS 的等待队列(CLH 队列)不是简单地“先进先出”,而是通过 hasQueuedPredecessors() 方法确保:只有当当前线程在队列中排在最前面(即没有前驱等待线程)时,才允许尝试获取锁。这意味着新来的线程不能插队,哪怕锁刚好释放,也必须老老实实排到队尾。
公平性体现在 tryAcquire 的前置检查
公平锁的 tryAcquire 实现会做两件事:
- 先判断 state 是否为 0(锁空闲)
- 再调用 hasQueuedPredecessors() —— 若返回 true(说明队列非空且当前线程不是队首),直接放弃 CAS 抢占,进入排队流程
这个检查发生在任何 CAS 操作之前,从根本上杜绝了“刚释放就抢到”的非公平行为。而非公平锁则跳过这步,直接尝试 CAS,失败后再排队。
队列结构本身不区分公平/非公平
AQS 的 CLH 队列是一个双向链表,FairSync 和 NonfairSync 共享同一套入队逻辑(addWaiter)。区别只在于:是否在 acquire 前多一次队列顺序校验。也就是说,两种模式下线程都加到队尾,但公平锁要求“轮到你才能拿”,非公平锁允许“看到就抢”。
公平性带来可预测性,但也牺牲吞吐量
公平策略避免了线程饥饿,尤其在高竞争场景下能保证长等待线程最终获得执行机会。但它增加了上下文切换和队列遍历开销——每次 lock() 都要检查前驱,CAS 失败率更高,整体吞吐通常低于非公平锁。实际选型应权衡响应确定性与性能需求。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











