公平锁
2025/10/19大约 3 分钟
一、什么是公平锁
公平锁(Fair Lock)是一种按照 线程请求锁的先后顺序 来分配锁资源的机制。
简单来说,谁先申请锁,谁就先获得锁,像排队买票一样遵循“先来先得”的原则。
在多线程环境下,公平锁能够确保每个线程都有机会依次获得锁,避免线程“饿死”(长期得不到执行机会)。
二、公平锁与非公平锁的区别
| 对比项 | 公平锁(Fair Lock) | 非公平锁(Non-Fair Lock) |
|---|---|---|
| 获取顺序 | 按请求先后顺序(FIFO)获取锁 | 新线程可直接竞争锁,可能插队成功 |
| 公平性 | 高(不会饿死线程) | 低(可能长期抢不到锁) |
| 性能 | 略低(需维护等待队列) | 较高(减少排队调度开销) |
| 使用场景 | 对公平性要求高的业务 | 对性能要求高的业务 |
在实际项目中,非公平锁更常用,因为它减少了线程切换的开销,提高系统吞吐量。
三、为什么需要公平锁
在高并发系统中,如果没有公平机制,某些线程可能长期抢不到锁,出现“线程饥饿(Starvation)”问题。
例如:
多个线程不断竞争同一资源时,非公平锁可能让部分线程反复抢占成功,而另一些线程长时间无法执行。
公平锁的设计目的是 让线程获取锁的顺序更可预期、更稳定,避免资源分配不均。
四、Java 中的公平锁实现原理
1. ReentrantLock 的公平与非公平模式
Java 提供的 ReentrantLock 支持两种模式:
// 公平锁
Lock fairLock = new ReentrantLock(true);
// 非公平锁(默认)
Lock unfairLock = new ReentrantLock(false);当构造参数为 true 时,表示启用公平锁机制。
2. 实现原理(基于 AQS 队列)
公平锁依赖于 AQS(AbstractQueuedSynchronizer)同步队列 实现:
- 当线程尝试获取锁时,AQS 会检查是否有其他线程在等待。
- 如果有,当前线程会被加入等待队列尾部,依次排队。
- 当锁释放时,AQS 会唤醒队列头部的下一个线程,让其获取锁。
这种机制保证了 锁的获取顺序与请求顺序一致,实现真正的公平性。
非公平锁则不同:线程在抢锁时可以直接尝试“插队”获取锁,即使前面还有其他等待的线程,从而提高性能但牺牲公平性。
五、公平锁的优缺点
优点:
- 保证公平性:线程按照先后顺序获取锁,不会饿死。
- 调度可预测:系统行为更稳定,适合对实时性或任务顺序有要求的业务场景。
缺点:
- 性能略低:线程必须排队,无法利用空闲 CPU 时间快速竞争锁。
- 上下文切换频繁:频繁唤醒与阻塞线程会增加系统开销。
六、应用场景
任务调度系统
确保任务按照提交顺序执行,避免后来的任务抢占先提交的任务资源。金融交易、订单系统
需要保证请求处理的严格顺序,避免出现处理乱序问题。日志写入、流水记录
对写入顺序要求严格,使用公平锁可确保顺序性与一致性。
七、总结
公平锁是一种 按请求顺序分配锁资源的机制,确保线程按照先后顺序依次获取锁,从而避免线程饥饿问题。
在 Java 中,ReentrantLock(true) 就是公平锁的典型实现。
虽然公平锁牺牲了部分性能,但在对执行顺序和公平性有要求的系统中,它能显著提升系统的稳定性与可预测性。