ReentrantLock 是一種支持支持重進入的鎖,它表示該鎖能夠支持一個線程對資源的重複加鎖,除此之外,該鎖還支持獲取鎖的公平性和非公平性選擇。
1 重入的實現
對於鎖的重入,我們來想這樣一個場景。當一個遞歸方法被sychronized關鍵字修飾時,在調用方法時顯然沒有發生問題,執行線程獲取了鎖之後仍能連續多次地獲得該鎖,也就是說sychronized關鍵字支持鎖的重入。對於ReentrantLock,雖然沒有像sychronized那樣隱式地支持重入,但在調用lock()方法時,已經獲取到鎖的線程,能夠再次調用lock()方法獲取鎖而不被阻塞。
如果想要實現鎖的重入,至少要解決一下兩個問題
- 線程再次獲取鎖:鎖需要去識別獲取鎖的線程是否爲當前佔據鎖的線程,如果是,則再次成功獲取。
- 鎖的最終釋放:線程重複n次獲取了鎖,隨後在n次釋放該鎖後,其他線程能夠獲取該鎖。鎖的最終釋放要求鎖對於獲取進行計數自增,計數表示當前鎖被重複獲取的次數,而鎖被釋放時,計數自減,當計數等於0時表示鎖已經釋放
(1) 鎖的獲取
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
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);//此時不需CAS
return true;
}
return false;
}
該方法通過當前線程是否爲獲取鎖的線程來決定獲取操作是否成功,如果是獲取鎖的線程再次請求,則將同步狀態值進行增加並返回true,表示獲取同步狀態成功。
(2)鎖的釋放
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方法必須返回false,而只有同步狀態完全釋放了,才能返回true。可以看出,該方法只有在同步狀態爲0的時候纔會返回true,並將佔有線程設置爲null,表示釋放成功。
2 公平鎖的實現
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
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;
}
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
這個方法就是在判斷同步隊列中當前節點是否有前驅節點,如果有則返回true,則表示有線程比當前線程更早地請求獲取鎖,因此需要等待前驅線程獲取並釋放鎖之後才能繼續獲取鎖。 由此就實現了公平鎖。