ReentrantLock的公平锁与非公平锁通过构造参数区分:默认非公平锁(new ReentrantLock()或new ReentrantLock(false)),公平锁需显式传true(new ReentrantLock(true));公平锁按FIFO排队防饥饿但吞吐较低,非公平锁允许插队提升性能但可能引发饥饿。

ReentrantLock 的公平锁与非公平锁策略,核心在于构造时的布尔参数和对业务场景的理解。默认就是非公平锁,想用公平锁必须显式传 true;但是否该用、何时该用,得看锁竞争模式和线程行为特征。
如何创建两种锁
创建方式非常直接:
- 非公平锁(默认):
ReentrantLock lock = new ReentrantLock(); - 公平锁(需显式指定):
ReentrantLock fairLock = new ReentrantLock(true);
注意:传 false 和不传参数效果一样,都是非公平锁。底层分别对应 NonfairSync 和 FairSync 类,它们在 tryAcquire 中的关键区别是——公平锁会先调用 hasQueuedPredecessors() 判断队列里有没有更早等待的线程,非公平锁则跳过这步,直接 CAS 尝试抢锁。
非公平锁更适合什么场景
适用于锁持有时间短、竞争不激烈、吞吐优先的场景:
立即学习“Java免费学习笔记(深入)”;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 高频小操作,比如计数器自增、状态位更新、缓存读写
- 临界区执行通常在纳秒到微秒级,线程抢到就走,极少排队
- JMH 测试中,这类场景下非公平锁常比公平锁快 10%–30%
它的高吞吐来自“插队”机制:刚释放锁的线程或新来的线程可立即 CAS 抢占,避免了入队、唤醒、上下文切换等开销。
公平锁适合哪些情况
当需要控制响应时间一致性、防止线程饥饿时考虑公平锁:
- 任务耗时较长(如 IO 等待、批量处理),且线程数不多
- 调度类逻辑,比如定时任务分发、资源配额轮询
- 混合负载中存在明显长短期任务,且不能容忍某类请求长期得不到服务(例如 Web 请求中夹杂导出任务)
公平锁按 FIFO 排队,能保证等待最久的线程优先获取,但代价是每次加锁都多一次队列检查,整体吞吐通常略低。
实际使用中的关键提醒
不要一刀切地全局启用公平锁。更务实的做法是:
- 默认保持非公平锁,仅在确认出现线程饥饿(如通过 JFR 或 Arthas 观察到 AQS 队列持续积压、平均等待时间陡增)时,才在关键路径局部改用公平锁
- 搭配
tryLock(long, TimeUnit)主动超时,既保留非公平锁的响应弹性,又避免无限等待 - 确保
lock()和unlock()成对出现在try-finally中,否则锁泄漏风险远高于公平性问题

















