公平锁在tryAcquire中调用hasQueuedPredecessors()检查队列是否为空或自身是否队首,非公平锁则直接CAS抢占;二者共用AQS的CLH双向队列,仅tryAcquire实现不同。

AQS(AbstractQueuedSynchronizer)是 Java 并发包中实现锁和同步器的核心框架,公平锁与非公平锁的差异,本质上体现在 AQS 的 tryAcquire 方法被调用时是否检查同步队列是否有等待线程——公平锁会先看队列是否为空,非公平锁则直接尝试抢占。
公平锁:先到先得,严格按队列顺序
公平锁在每次尝试获取锁时,都会调用 hasQueuedPredecessors() 判断自己前面是否有等待者:
- 如果返回
true(即队列不空,且有其他线程在排队),当前线程放弃抢锁,进入队列尾部等待; - 只有队列为空或自己是队首时,才继续执行 CAS 尝试获取 state;
- 这个检查发生在
ReentrantLock.FairSync.tryAcquire()中,是公平性的关键逻辑。
非公平锁:插队优先,不管队列有没有人
非公平锁的 tryAcquire() 实现跳过了队列前置检查:
- 上来就用 CAS 尝试修改 state,成功就直接获得锁(即使队列里有人在等);
- 失败后才去判断是否需要入队(比如重入、或者调用
acquireQueued); - 这就是“插队”行为的来源——刚释放锁的线程可能立刻再次抢到,而排队线程还得继续等。
底层共用同一套 AQS 队列结构
无论公平还是非公平,它们都依赖 AQS 的 CLH 变种队列(FIFO 双向链表)管理阻塞线程:
立即学习“Java免费学习笔记(深入)”;
- 节点入队、唤醒、取消等操作完全一致,由 AQS 统一维护;
- 区别仅在于“是否允许在队列非空时还尝试抢锁”,也就是
tryAcquire的实现不同; - 所以 ReentrantLock 构造时传入
true(公平)或false(非公平),实际是切换内部Sync子类。
一个典型对比场景
假设线程 T1 持有锁并释放,此时 T2 正在队列中等待,T3 同时发起获取请求:
- 公平模式下:T3 调用
tryAcquire→ 发现 T2 在队列中 → 入队排在 T2 后面; - 非公平模式下:T3 调用
tryAcquire→ 直接 CAS 抢锁 → 成功拿到,T2 继续等待。
这种设计让非公平锁吞吐量更高(减少线程上下文切换),但可能造成饥饿;公平锁保障等待顺序,代价是性能略低。


















