reentrantlock通过fair参数控制公平性:公平锁(new reentrantlock(true))严格按fifo顺序,每次lock()先检查队列是否有前驱节点,有则入队;非公平锁(默认)直接cas抢占,失败后才入队,提升吞吐但可能导致饥饿。

ReentrantLock 通过构造函数的 fair 参数控制是否启用公平策略,底层靠同步队列(AQS 的 CLH 队列)和线程抢占逻辑实现差异,核心不在锁本身,而在获取锁时是否检查队列是否有等待者。
公平锁:先到先得,严格按排队顺序
创建方式:new ReentrantLock(true)。每次调用 lock() 时,它会先检查 AQS 同步队列中是否有前置等待节点:
- 若有等待者(即
!hasQueuedPredecessors()为 false),当前线程直接入队,不抢锁; - 若队列为空或自己是队首,则尝试 CAS 获取 state;
- 即使当前锁空闲,也不会让刚唤醒/新来的线程“插队”,确保 FIFO 语义。
非公平锁(默认):允许插队,提升吞吐量
创建方式:new ReentrantLock() 或 new ReentrantLock(false)。获取锁分两步:
- 第一步:不管队列状态,直接 CAS 尝试抢占锁(
nonfairTryAcquire); - 第二步:抢占失败后,才走标准 AQS 入队流程(调用
acquire),加入同步队列等待唤醒。
这意味着:一个刚释放锁的线程,可能被另一个还没进队、但恰好执行到第一步的线程“截胡”,造成实际调度不按排队顺序。
关键区别不在加锁,而在 tryAcquire 的实现逻辑
公平与非公平的差异完全体现在 FairSync 和 NonfairSync 两个内部类的 tryAcquire(int acquires) 方法中:
-
FairSync.tryAcquire开头就调用hasQueuedPredecessors(),有前驱则返回 false,强制入队; -
NonfairSync.tryAcquire跳过该检查,优先 CAS 抢占,失败后再入队; - 释放锁(
unlock())逻辑两者完全一致,都调用 AQS 的release,唤醒队首节点。
选择建议:公平性 vs 性能
公平锁能避免线程饥饿,适合对响应时间一致性要求高的场景(如实时任务调度),但频繁检查队列+更多线程阻塞会降低吞吐;非公平锁在多数业务场景下性能更好,因为减少了不必要的上下文切换和队列操作,JVM 默认也倾向这种行为模式。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











