reentrantlock默认是非公平锁,启用公平锁需显式传true;非公平锁吞吐高但可能饥饿,公平锁fifo保障公平但开销大,二者构造后不可切换。

ReentrantLock 默认是非公平锁,但可通过构造函数显式指定为公平锁。是否启用公平性,本质是在吞吐量和线程调度公平性之间做权衡:非公平锁吞吐更高,但可能引发个别线程长期等待;公平锁保障 FIFO 顺序,避免饥饿,但上下文切换和队列管理开销更大。
如何创建公平锁与非公平锁
ReentrantLock 提供两个构造方法:
-
非公平锁(默认):
new ReentrantLock()或new ReentrantLock(false) -
公平锁:
new ReentrantLock(true)
一旦实例化,锁的公平性不可更改。注意:公平性只影响“等待获取锁的线程”的排队策略,不影响已持有锁的线程重入行为(ReentrantLock 始终支持重入)。
公平锁如何缓解线程饥饿
在公平模式下,锁维护一个 FIFO 等待队列。当锁释放时,仅唤醒队列头节点的线程;新线程即使能立刻抢到锁,也必须先入队、等待轮到自己。这确保了等待时间最长的线程优先获得执行机会。
例如:多个线程频繁争抢同一把锁,若长期使用非公平锁,高优先级或运气好的线程可能持续抢占,导致低频线程迟迟得不到调度——公平锁可有效抑制这类饥饿现象。
非公平锁为何吞吐更高
非公平锁允许刚释放锁的线程或新到来的线程直接尝试 CAS 抢占,无需入队。这种“插队”行为减少了线程阻塞/唤醒次数和队列操作,尤其在锁持有时间短、竞争不激烈时,显著降低延迟。
典型场景如:高频小粒度同步块(如计数器自增),用非公平锁通常比公平锁快 10%–30%(JMH 测试常见结果)。
实际配置建议
不要全局统一设为公平锁。应结合业务特征判断:
- 若任务耗时长、线程数少、对响应时间一致性要求高(如定时任务调度器),考虑用 公平锁
- 若锁竞争频繁但临界区极短(如缓存读写、状态标记更新),优先选 非公平锁
- 存在明显长短期任务混合,且需防止单一线程被饿死(如 Web 请求处理中混有批量导出任务),可在关键路径加公平锁,或配合超时获取(
tryLock(long, TimeUnit))主动降级
必要时通过 JFR 或 Arthas 观察 AbstractQueuedSynchronizer$Node 队列长度与平均等待时间,验证是否真出现饥饿而非单纯性能瓶颈。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











