公平锁吞吐量通常低于非公平锁,因其强制线程排队并多次volatile读、park阻塞及链表探查,引入额外同步开销与上下文切换;非公平锁跳过队列检查、避免阻塞,执行路径更轻量。

公平锁吞吐量通常低于非公平锁,核心原因在于它强制引入了额外的同步开销和线程调度成本,而非公平锁通过“能抢则抢”的策略绕开了这些瓶颈。
多一次 volatile 读,就多一次缓存竞争
公平锁每次尝试获取锁前,必须调用 hasQueuedPredecessors() 判断队列状态。这个方法至少触发两次 volatile 读:一次读 AQS 的 head 节点,一次读 tail 节点。在多核 CPU 上,频繁读同一 volatile 地址会引发缓存行失效(cache line invalidation),导致总线争用加剧。非公平锁跳过这一步,只做一次 volatile 写(CAS 修改 state),访问路径更短、更轻量。
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
每次 acquire 都要 park,代价是微秒级上下文切换
公平锁要求新线程必须排队,哪怕队列为空也要走完整流程:
– 创建 Node 实例
– CAS 更新 tail 指针
– 设置前驱节点 waitStatus = SIGNAL
– 调用 LockSupport.park() 进入 WAITING 状态
这些操作涉及用户态到内核态切换、内存分配、链表遍历,单次开销常达数微秒。非公平锁在锁刚释放瞬间,常被下一个线程直接抢中,全程停留在用户态,无阻塞、无调度介入。
队列检查不是“查一下就行”,而是链表探查
hasQueuedPredecessors() 并非简单判断队列是否为空。它要检查 head 是否等于 tail,还要确认 head.next 是否为当前线程——这意味着即使队列实际为空,也要完成一次链表结构访问。而非公平锁的“快路径”完全不触碰队列结构,只要 state == 0 就直接 CAS,物理执行路径更干净。
饥饿只是理论风险,性能损失却是实打实的
非公平锁并非永远不排队,它只是把“排队”推迟到抢失败之后;现实中只要临界区超过几十纳秒,队列中的线程基本都能轮到执行。但公平锁的“绝对顺序”代价是:每个线程无论能否立即执行,都得走一遍 park/unpark。压测显示,高竞争下公平锁吞吐量常比非公平锁低 50% 以上——这不是设计缺陷,而是硬件与 JVM 协同作用下的真实损耗。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










