ReentrantLock 的公平锁与非公平锁
ReentrantLock 是 Java 中重要的可重入互斥锁,它提供了两种锁模式:公平锁和非公平锁。

核心区别
| 特性 | 非公平锁(默认) | 公平锁 |
|---|---|---|
| 获取锁顺序 | 允许插队 | 严格按照FIFO队列顺序 |
| 性能 | 更高(减少线程唤醒开销) | 较低(上下文切换更多) |
| 避免饥饿 | 可能产生线程饥饿 | 不会饥饿 |
| 适用场景 | 大部分业务场景 | 对公平性有严格要求的场景 |
源码实现对比
非公平锁的实现
// NonfairSync.java
final void lock() {
// 直接尝试抢占锁,不检查等待队列
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1); // 获取失败才进入队列
}
protected final boolean tryAcquire(int acquires) {
// 调用父类的nonfairTryAcquire
return nonfairTryAcquire(acquires);
}
// AbstractQueuedSynchronizer.java
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 即使有线程在等待队列中,也允许直接抢占
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 可重入逻辑...
}
公平锁的实现
// FairSync.java
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 关键区别:先检查是否有前驱节点在等待
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 可重入逻辑...
}
公平锁的判断方法
// 检查是否有其他线程在当前线程之前等待
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
这个方法返回 true 表示有排在当前线程前面的线程:
- 队列不为空(
h != t) - 队列中第一个等待节点不是当前线程
性能差异分析
非公平锁为什么性能更高?
- 减少上下文切换:被唤醒的线程需要上下文切换,新到的线程如果直接获取锁,可以避免这次上下文切换
- 减少线程挂起/唤醒:非公平锁允许新线程插队,避免了部分线程进入等待队列
使用示例
public class ReentrantLockDemo {
// 创建公平锁和非公平锁
private static ReentrantLock fairLock = new ReentrantLock(true);
private static ReentrantLock unfairLock = new ReentrantLock(false); // 默认
public static void testLock(ReentrantLock lock, String lockType) {
for (int i = 0; i < 3; i++) {
int threadId = i;
new Thread(() -> {
for (int j = 0; j < 5; j++) {
lock.lock();
try {
System.out.println(lockType + " - 线程" + threadId +
" 第" + j + "次获取锁");
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}, "Thread-" + i).start();
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("=== 公平锁测试 ===");
testLock(fairLock, "公平锁");
Thread.sleep(3000);
System.out.println("\n=== 非公平锁测试 ===");
testLock(unfairLock, "非公平锁");
}
}
选择建议
- 默认使用非公平锁:大部分场景下,非公平锁的性能更好,能满足需求
- 使用公平锁的场景:
- 对锁获取顺序有严格要求的业务
- 避免线程饥饿
- 响应时间要求非常均匀
- 权衡考虑:公平锁虽然保证了公平性,但会牺牲一定的吞吐量
可以通过 lock.isFair() 方法判断当前锁的模式。