reentrantlock的公平锁与非公平锁核心差异在于aqs队列抢占逻辑:公平锁严格fifo,非公平锁允许插队;二者共享aqs机制,仅tryacquire实现不同,构造时选定且不可更改。

ReentrantLock 的公平锁与非公平锁,核心差异不在锁本身,而在于 AQS(AbstractQueuedSynchronizer)队列的抢占逻辑 —— 公平锁严格遵循 FIFO 入队顺序,非公平锁允许新线程“插队”尝试直接获取锁。
公平锁:每次 acquire 都先检查队列是否为空或自己是否是队首
公平锁的 tryAcquire 方法(由 FairSync 实现)会做两件事:
- 先判断 AQS 同步队列是否为空(
!hasQueuedPredecessors()),即当前没有其他线程在排队; - 再检查自己是不是队列中第一个等待者(即前驱节点是否为 head);
- 只有满足“队列空”或“自己排在最前面”,才尝试 CAS 设置 state;否则直接入队等待。
这个 hasQueuedPredecessors() 是关键:它通过读取队列 head 和 tail 引用,判断是否存在比当前线程更早排队的节点。即使只是 volatile 读,也足以保证可见性与顺序语义。
非公平锁:抢锁不看队列,失败后再入队
非公平锁(NonfairSync)的 lock() 方法第一步就是直接 CAS 尝试获取锁(compareAndSetState(0, 1)),完全绕过队列状态检查。
- 成功 → 直接获得锁,设置 owner 为当前线程,不进队;
- 失败 → 才调用
acquire(1)走 AQS 标准流程:先 tryAcquire(此时也会检查 state 和 owner,但不检查队列顺序),失败则入队、挂起。
也就是说,非公平锁在“刚释放锁的瞬间”,新来的线程大概率能抢到(避免上下文切换开销),而排队中的线程可能被连续“插队”,造成饥饿——但实际场景中吞吐量更高。
底层共用同一套 AQS 队列机制,区别只在 tryAcquire 的实现
公平锁和非公平锁都继承自 Sync(AQS 子类),共享同一套 CLH 队列、Node 结构、park/unpark 等机制。
- 锁的可重入性由
state计数 +Thread.currentThread()判断 owner 实现,两者完全一致; - 条件队列(ConditionObject)、中断响应、超时等待等高级功能,也不受公平性影响;
- 唯一不同的是
tryAcquire(int acquires)这个钩子方法:公平版加了队列前置检查,非公平版没有。
构造时决定,运行时不可切换
ReentrantLock(boolean fair) 构造时就选择使用 FairSync 或 NonfairSync 实例,后续所有操作都绑定该实现。
- 默认构造函数传
false→ 非公平锁(性能优先); - 显式传
true→ 公平锁(确定性优先); - 一旦创建,无法动态修改公平策略 —— 因为 sync 对象是 final 字段,且 AQS 内部状态逻辑已固化。
不复杂但容易忽略:所谓“公平”,只是对等待队列中线程的调度公平,并不保证 CPU 时间片、线程优先级或系统调度层面的绝对公平。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











