非公平锁吞吐量更高,因其在lock()和tryAcquire()中直接CAS抢锁,跳过入队、tail更新、waitStatus设置及线程挂起,大幅减少高竞争下的开销;公平锁则必须依赖waitStatus验证排队资格,每步均涉及volatile读、CAS和对象分配,成本显著更高。

非公平锁吞吐量更高,根本原因不是“插队”本身,而是它大幅减少了 Node 实例创建、tail 指针 CAS 更新、waitStatus 维护和线程挂起/唤醒的频次——这些操作在高竞争下会快速放大开销。
非公平锁怎么跳过 Node 入队直接抢锁
非公平锁在 lock() 和 tryAcquire() 两个关键入口都执行一次 compareAndSetState(0, 1)。只要 state == 0,就尝试抢占,完全不查同步队列是否为空、也不管 head.next 是谁。
这意味着:
- 刚释放锁的瞬间,若恰好有线程执行
lock(),大概率直接抢到,根本不会走到addWaiter() - 即使队列里已有多个
Node处于SIGNAL状态,新线程仍可“穿插”成功 - 整个过程不新建
Node、不更新tail、不设置前驱waitStatus = SIGNAL、不调用LockSupport.park()
公平锁为什么必须依赖 waitStatus 判断排队资格
公平锁的 tryAcquire() 在 state == 0 时,强制调用 hasQueuedPredecessors() ——这个方法本质是检查 head.next 是否存在且其 waitStatus != CANCELLED。
它不是简单“看队列空不空”,而是读取链表结构并验证节点状态的有效性:
- 如果
head.next是CANCELLED节点,要继续往后找第一个有效Node - 只要找到任意一个有效后继,就说明“有人排在我前面”,必须放弃 CAS、调用
addWaiter()入队 - 入队后还要再 CAS 设置前驱的
waitStatus = SIGNAL,才能安全进入park()
每一步都涉及 volatile 读、CAS、对象分配,比一次 compareAndSetState() 重得多。
使用一条命令部署ProbeChain Rydberg测试网代理节点。自动注册为Agent(NodeType=1),免gas,支持macOS/Linux/Windows。触发词:/r
waitStatus 不只是标记,而是调度决策开关
Node.waitStatus 的值(如 0、SIGNAL、CANCELLED)直接影响线程是否被唤醒、何时被唤醒、甚至是否被跳过。
例如 unparkSuccessor() 唤醒后继时:
- 必须遍历
next链表,跳过所有waitStatus == CANCELLED的节点 - 对首个非
CANCELLED节点,先CAS将其waitStatus改为0,再LockSupport.unpark() - 非公平锁在
release()后若发现state已被新线程抢走,连这一步都省了
公平性越强,对 waitStatus 一致性和链表结构完整性的依赖就越紧——一旦发生虚假唤醒或 CANCEL 不彻底,队列就可能卡住或跳过本该唤醒的线程。
实测中 Node 入队率差异直接反映 GC 与上下文切换压力
在 100 线程争抢单许可锁的压测场景下:
- 非公平锁平均
Node入队率约 30%,意味着 70% 的请求在线程栈上完成,没触发任何队列逻辑 - 公平锁接近 95%,几乎每次争抢都新建
Node、更新tail、设置waitStatus、挂起线程 - 这导致公平锁的 GC 压力明显更高,且 CPU 花费大量时间在
park()/unpark()和上下文切换上,而非实际业务计算
真正容易被忽略的是:waitStatus 的维护不是免费的——它是公平性得以成立的隐式成本,而这个成本在低竞争时几乎不可见,一到高并发就指数级暴露。


















