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() 方法判断当前锁的模式。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播