【并发编程】公平锁与非公平锁的区别
在Java的concurrent包中,公平锁与非公平锁是很常见的概念,ReentrantLock、ReadWriteLock默认都是非公平模式。
非公平锁的效率为什么会高于公平锁呢?那么公平锁与非公平锁又有什么区别呢?、
概念解释
首先从字面意思理解,公平锁自然是遵循FIFO(先进先出)原则的,先到的线程会优先获取资源,后到的会进行排队等待,而非公平锁是不遵循这个原则的。
图解
tip: 该图出自 https://www.jianshu.com/p/f584799f1c77
源码解析
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
- 可以看到ReentrantLock默认使用的是非公平锁。
- 公平锁和非公平锁分别使用了FairSync与NonfairSync实现
非公平锁
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
- 可以看到非公平锁里,判断当前锁占用状态==0直接会进行compareAndSetState尝试获取锁。若此时有线程排队,可能争夺不过资源。所以这是非公平的
- 在非公平锁里,因为可以直接compareAndSetState来获取锁,不需要加入队列,然后等待队列头线程唤醒再获取锁这一步骤,所以效率较快
公平锁
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;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
- 在公平锁里,判断当前锁占用状态==0后,会继续判断hasQueuedPredecessors,即当前队列是否有排队的情况,如果没有才会尝试获取锁
- 这样可以保证遵循FIFO的原则,每一个先来的线程都可以最先获取到锁,但是增加了上下文切换与等待线程的状态变换时间。所以效率相较于非公平锁较慢。
时在中春,阳和方起