公平锁与非公平锁的核心区别在于锁空闲时是否允许插队:公平锁严格按等待队列顺序获取,非公平锁允许直接抢占;ReentrantLock默认非公平(new ReentrantLock()等价于new ReentrantLock(false)),需显式传true启用公平模式;非公平锁性能通常高10%–30%,公平锁仅在强顺序、已现饥饿或低竞争场景适用。

公平锁和非公平锁的核心区别在「抢锁时机」
不是“谁先启动线程谁先拿到锁”这种表面顺序,而是看线程在锁空闲的瞬间是否允许“插队”。
公平锁会严格检查:当前有没有其他线程在排队?自己是不是队头?只有满足才拿锁。
非公平锁则不管排队情况,只要锁一空闲,就立刻 compareAndSetState(0, 1) 尝试抢占——哪怕队列里已有 5 个线程在等。
ReentrantLock 构造参数决定行为,但默认是非公平的
很多人以为 new ReentrantLock() 是“中性”的,其实它等价于 new ReentrantLock(false),即明确启用非公平模式。
要切到公平模式,必须显式传 true:new ReentrantLock(true)。
这个布尔值只影响锁的同步器实现类(FairSync vs NonfairSync),一旦实例化无法更改。
性能差异真实存在,尤其在高并发争抢场景
非公平锁吞吐量通常高出 10%–30%,因为省去了每次抢锁前对等待队列的检查(hasQueuedPredecessors());
公平锁虽然避免了饥饿,但每个释放锁的操作都要唤醒队头线程,且唤醒后还需重新调度,带来额外上下文切换开销。
常见误判:
- 认为“公平=更安全”——其实数据一致性不依赖公平性,而依赖锁的互斥语义
- 在日志打印、计数器等低竞争场景强行用公平锁——毫无收益,纯增开销
- 把
synchronized当成可配置的公平锁——它底层是 JVM 实现的非公平锁,无法改为公平
什么时候该选公平锁?别靠感觉,看这三点
公平锁不是“更高级”的选项,而是特定约束下的妥协方案:
- 业务逻辑要求强顺序性,比如任务调度器必须按提交时间执行,不能让后提交的高优先级任务反复插队
- 已观察到明显饥饿现象:某个线程长期
WAITING在AbstractQueuedSynchronizer$ConditionObject.await()上,且堆栈始终卡在锁获取路径 - 系统负载低、锁竞争不激烈(如配置加载、初始化阶段),此时公平性开销占比小,可接受
ReentrantLock 和 synchronized 默认策略的根本原因。立即学习“Java免费学习笔记(深入)”;








