Java中的鎖——重入鎖ReentrantLock
阿新 • • 發佈:2019-02-17
ReentrantLock 是一種支援支援重進入的鎖,它表示該鎖能夠支援一個執行緒對資源的重複加鎖,除此之外,該鎖還支援獲取鎖的公平性和非公平性選擇。
1 重入的實現
對於鎖的重入,我們來想這樣一個場景。當一個遞迴方法被sychronized關鍵字修飾時,在呼叫方法時顯然沒有發生問題,執行執行緒獲取了鎖之後仍能連續多次地獲得該鎖,也就是說sychronized關鍵字支援鎖的重入。對於ReentrantLock,雖然沒有像sychronized那樣隱式地支援重入,但在呼叫lock()方法時,已經獲取到鎖的執行緒,能夠再次呼叫lock()方法獲取鎖而不被阻塞。
如果想要實現鎖的重入,至少要解決一下兩個問題
- 執行緒再次獲取鎖:鎖需要去識別獲取鎖的執行緒是否為當前佔據鎖的執行緒,如果是,則再次成功獲取。
- 鎖的最終釋放:執行緒重複n次獲取了鎖,隨後在n次釋放該鎖後,其他執行緒能夠獲取該鎖。鎖的最終釋放要求鎖對於獲取進行計數自增,計數表示當前鎖被重複獲取的次數,而鎖被釋放時,計數自減,當計數等於0時表示鎖已經釋放
(1) 鎖的獲取
下面來看看非公平鎖的重入實現,它的實現在自定義同步器Sync內部nonfairTryAcquire方法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)鎖的釋放
現在來看看鎖的釋放,它同樣是定義在自定義同步器Sync內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 公平鎖的實現
所有公平性,就是在絕對時間上,先對鎖進行獲取的請求一定先被滿足。也就是等待時間最長的執行緒最優先獲取鎖 公平鎖的實現在同步器FairSync內/**
* 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;
}
該方法與非公平鎖的獲取唯一不同就是判斷條件多了一個hasQueuedPredecessors方法,這個方法是在AbstractQueuedSynchronizer中定義
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,則表示有執行緒比當前執行緒更早地請求獲取鎖,因此需要等待前驅執行緒獲取並釋放鎖之後才能繼續獲取鎖。 由此就實現了公平鎖。