10 公平锁和非公平锁

vvEcho 2025-03-01 14:09:00
Categories: Tags:

Java中提供的synchronized只能是非公平锁。

Java中提供的ReentrantLock,ReentrantReadWriteLock可以实现公平锁和非公平锁

公平锁

公平锁,先到先得;多个线程按顺序排队获取锁,每个线程获取机会均等,但永远只有队列首位线程能获取到锁。

优点:每个线程等待一段时间后,都有执行的机会,不至于出现某个线程饿死在队列中。
缺点:是队列里面除了第一个线程,其他的线程都会阻塞,cpu 唤醒阻塞线程的开销会很大

非公平锁

多个线程(不管是不是队列首位)去获取锁时会尝试直接获取锁,能获取到就执行任务,否则乖乖排队。代表实现是CAS比较并交换;

优点:获取锁更加灵活、吞吐量大、减少CPU唤醒线程的开销。
缺点:会出现某些线程永远获取不到锁,饿死在队列中,最终由 JVM 回收

注意锁的获取有三个条件:看状态state是否是0,当前线程是否为null; 是否排在队首
公平锁跟非公平锁加锁的逻辑差不多,唯一就是公平加锁的 if 判断中多了 hasQueuedPredecessors 是否队首

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 多了 hasQueuedPredecessors 是否队首判断
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
} else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}

推荐博客:公平锁和非公平锁

公平锁非公平锁对竞争的影响?

公平锁降低吞吐、减少饥饿;
非公平锁提高吞吐、放大竞争,但可能导致饥饿

公平锁的竞争模型

先到先得(FIFO)
实现方式(以 ReentrantLock 为例):

竞争特征

非公平锁的竞争模型

只要你抢得到,你就赢

实现方式:

竞争特征

公平锁非公平锁使用场景?

适合公平锁的场景:

对于吞吐 > 公平的业务场景,推荐使用非公平锁

总结
公平锁和非公平锁的核心区别在竞争策略。
公平锁严格 FIFO,降低饥饿但牺牲吞吐;
非公平锁允许插队,通过减少线程切换提高性能。
在高并发核心链路中通常选非公平锁,在需要响应保证或防止长期饥饿的场景才用公平锁