Java 顯示鎖 之 重入鎖 ReentrantLock(七)
ReentrantLock 重入鎖簡介
重入鎖 ReentrantLock,顧名思義,就是支援同一個執行緒對資源的重複加鎖。另外,該鎖還支援獲取鎖時的公平與非公平性的選擇。
重入鎖 ReentrantLock,只支援獨佔方式的獲取操作,因此它只實現了 tryAcquire、tryRelease 和 isHeldExclusively 方法。
ReentrantLock 如何實現鎖重入
鎖重入是指任意執行緒在獲取到鎖之後,能夠再次獲取該鎖而不會被鎖所阻塞,該特性的實現需要解決兩個問題:
1、執行緒再次獲取鎖
需要識別獲取鎖的執行緒是否為當期佔有鎖的執行緒,如果是,則便獲取鎖成功
2、鎖最終得到釋放
同一個執行緒重複 n 次獲得了鎖,在第 n 次釋放該鎖後,其它執行緒能夠獲取到該鎖。鎖的最終釋放要求鎖對於獲取進行計數,計數表示當前鎖被重複獲取的次數,而鎖被釋放時,計數自減,當計數等於0時,表示鎖已經成功釋放
ReentrantLock 是通過自定義同步器來實現所得獲取和釋放,預設使用非公平性,下面就以非公平性獲取鎖為例,學習重入鎖是如何實現的:
final boolean nonfairTryAcquire(int acquires) {
//當前執行緒
final Thread current = Thread.currentThread();
//當前同步狀態
int c = getState();
//當前同步狀態等於0,說明當前執行緒可以獲取同步狀態
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;
}
通過判斷當前執行緒是否為獲取鎖的執行緒來決定獲取操作是否成功,如果是獲取鎖的執行緒,則將同步狀態值進行累加並返回true,表示獲取同步狀態成功。
成功獲取鎖的執行緒再次獲取鎖,只是增加了同步狀態值,這也就要求在釋放同步狀態時遞減同步狀態的值。如下,釋放鎖的程式碼:
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
如果該鎖被獲取了n次, 那麼前( n- 1) 次 tryRelease( int releases) 方法必須返回false,只有同步狀態完全釋放了,才能返回tru。 該方法將同步狀態 是否為 0 作為作為最終釋放的條件,當同步狀態為0時,將該鎖的擁有者執行緒設定為null,並返回true,表示成功釋放。
ReentrantLock 如何實現鎖的公平性與非公平性
公平性與否是針對獲取鎖而言的,如果一個鎖時公平的,那麼鎖的獲取順序就應該服務請求時間的順序,也就是FIFO。
由上文介紹的非公平獲取鎖方法 nonfairTryAcquire( int acquires) 可知,只要CAS設定同步狀態成功,則表示執行緒獲取了鎖,而公平鎖則不同,如程式碼所示:
static final class FairSync extends Sync {
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;
}
}
該方法與 nonfairTryAcquire( int acquires) 的區別在於多了hasQueuedPredecessors() 方法,即加入了同步佇列中當前節點是否有前驅節點的判斷,如果該方法返回true,則表示有執行緒比當前執行緒更早的請求了鎖,因此需要等待前驅執行緒獲取並釋放鎖之後才能繼續獲取鎖。