在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的原則,每一個先來的線程都可以最先獲取到鎖,但是增加了上下文切換與等待線程的狀態變換時間。所以效率相較於非公平鎖較慢。