前言
重入鎖ReentrantLock有兩種方式獲取鎖,一種是公平性,一種是非公平性。如果在絕對時間上,先請求獲取鎖的線程一定會先獲取到鎖,那麼這個鎖就是公平的,反之,這個鎖就是不公平的。公平的獲取鎖,也就是等待時間最長的線程最優先獲取鎖,也可以說鎖獲取是順序的。那重入鎖ReentrantLock如何實現公平鎖,以及非公平鎖,它們的區別又是什麼?
正文
重入鎖ReentrantLock默認使用非公平獲取鎖,同時提供構造函數,能夠控制鎖是否是公平。
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
公平鎖使用FairSync同步組件,非公平鎖使用NonfairSync同步組件。FairSync與NonfairSync均是ReentrantLock的內部類,又共同繼承了ReentrantLock的一個內部類Sync。FairSync與NonfairSync不同的地方在於重寫Sync的lock()方法與tryAcquire()方法。我們先看一下重入鎖ReentrantLock默認的非公平鎖如何獲取鎖。
非公平獲取鎖
非公平性獲取鎖調用NonfairSync的lock()方法
final void lock() {
// 如果當前同步狀態爲O,表示鎖未被任何線程獲取,CAS設置同步狀態爲1
if (compareAndSetState(0, 1))
// 設置當前線程爲獲取鎖的線程
setExclusiveOwnerThread(Thread.currentThread());
else
// 如果鎖已經被某線程獲取,嘗試獲取鎖
acquire(1);
}
NonfairSync的lock()方法會調用同步器acquire方法,最終會調用NonfairSync的nonfairTryAcquire方法來獲取同步狀態。
final boolean nonfairTryAcquire(int acquires) {
// 獲取當前線程
final Thread current = Thread.currentThread();
// 獲取同步狀態
int c = getState();
// 同步狀態爲0,表示沒有線程獲取鎖
if (c == 0) {
// CAS獲取同步狀態
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;
}
nonfairTryAcquire會判斷同步狀態是否爲0,如果同步狀態是0,說明目前沒有任何線程獲取到鎖,設置獲取鎖的線程爲當前線程並返回true,表示同步狀態成功。當同步狀態不爲0時,判斷當前線程是否爲獲取鎖的線程,如果是獲取鎖的線程再次請求,則將同步狀態的值進行增加並返回true,表示同步狀態成功。
在非公平性獲取鎖中,只要CAS設置同步狀態成功,則表示當前線程獲取了鎖。那公平性是如何獲取鎖?
公平性獲取鎖
公平性獲取鎖調用FairSync的lock()方法
final void lock() {
// 獲取鎖
acquire(1);
}
FairSync的lock()方法調用同步器acquire方法,最終會調用FairSync的tryAcquire方法來獲取同步狀態。
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 同步狀態爲0,表示沒有線程獲取鎖
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;
}
}
我們把該方法與非公平獲取鎖時調用的nonfairTryAcquire相比較,唯一不同的位置在於當同步狀態爲0時,增加hasQueuedPredecessors判斷,而該方法判斷在同步隊列中當前節點是否有前驅節點,如果該方法返回true,表示有線程比當前線程更早的請求獲取鎖,因此需要等待前驅線程獲取並釋放鎖後才能繼續獲取鎖。
注:hasQueuedPredecessors方法爲同步器AbstractQueuedSynchronizer中的方法,鎖的語義均由同步器AbstractQueuedSynchronizer實現。
public final boolean hasQueuedPredecessors() {
// 同步隊列尾節點
Node t = tail;
// 同步隊列頭節點
Node h = head;
Node s;
// 判斷頭節點的後繼節點的線程是否爲當前線程,即判斷當前節點是否有前驅節點
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
重入鎖ReentrantLock在獲取鎖時通過調用hasQueuedPredecessors方法來實現公平鎖的語義。