题目:公平锁和非公平锁的区别?
难度:low
标签:java/lock, java/concurrency
评分:⭐⭐☆☆☆(2/5)
公平锁和非公平锁是依据锁释放后唤醒阻塞中线程具体谁来执行进行划分的;
按照哪个线程等待时间长哪个线程执行就是公平锁,也就是给线程按时间排序,先来后到;
非公平锁是唤醒所有等待线程,大家争抢,谁抢到了谁就执行,看的是线程调度优先级;
java中synchronized升级为重量级锁后是非公平锁,使用aqs的reechangelock可以自己选择公平锁或者非公平锁,在创建锁的时候可以设置;
非公平锁可以让更快的线程执行,但是可能会有一些线程一直执行不到的情况;
优点:抓住"先来后到 vs 争抢"的本质差异;知道 ReentrantLock 构造器可切公平性;知道非公平会饥饿
不足:① "非公平锁唤醒所有等待线程"是硬伤——释放锁只 unpark 后继一个节点,非公平指的是新来的线程可以插队(barging),不是惊群;② 公平锁判据不是"等待时间",而是 hasQueuedPredecessors()——队列里有没有前驱;③ 说"看线程调度优先级"错,OS 优先级与锁竞争无关;④ 漏了公平锁吞吐更低、非公平默认(ReentrantLock 默认 false)
补充:公平锁 = 严格 FIFO + 无插队;非公平锁 = 允许新线程直接 CAS 抢,抢不到再入队;差距在高竞争时才明显(吞吐差 10%~100%)
核心考点定位:能否说清"公平"的判据是队列前驱判定,而非等待时长;非公平的收益与代价。
知识点拆解:
FairSync.tryAcquire 先调 hasQueuedPredecessors()——队列中是否存在排在当前线程之前的节点;有则入队,无则 CAS 抢NonfairSync.tryAcquire 直接 compareAndSetState(0, 1),成功即插队;失败才走 acquireQueued 入队阻塞unparkSuccessor(h) 只唤醒一个后继,不是全部(惊群在 AQS 里被刻意避免)按「底层原理 → 设计权衡 → 工程实践」递进排列。
考察方向:
hasQueuedPredecessors 的判定逻辑与 barging 时机延伸递进面试题:
tryLock() 不带超时的插队语义、以及条件队列转移仍可能造成延迟【一句话结论】
面试官您好,公平与非公平的差别就一句话:抢锁前要不要先看队列里有没有人在排队。
【三点展开】
第一,公平锁的 tryAcquire 会先调 hasQueuedPredecessors(),只要前面还有人排队就老老实实入队,严格 FIFO,不会插队。
第二,非公平锁直接 CAS 把 state 从 0 改成 1,抢到就跑,抢不到才入队——所以"非公平"说的是允许新线程插队,不是唤醒所有线程一起抢,释放时其实只唤醒一个后继节点。
第三,这个插队是有意为之:刚释放锁的线程还在 CPU 上、缓存是热的,让它继续跑能省掉一次上下文切换,所以非公平吞吐更高;代价是极端情况下远端线程可能一直抢不到,产生饥饿。
【一句边界】
当然,如果业务对顺序敏感,比如库存扣减、订单状态机,就该用 new ReentrantLock(true);普通缓存、计数器场景没必要为公平付吞吐代价,默认非公平就好。