前言
重入鎖ReentrantLock,就是支持線程重進入的鎖,它表示該鎖能夠支持一個線程對資源的重複加鎖。synchronized關鍵字隱士的支持鎖的可重入性。利用synchronized,執行線程在獲取鎖之後仍然可以多次獲取鎖。而ReentrantLock是通過顯示去完成鎖的可重入性,接下來我們探討ReentrantLock如何實現可重入性。
正文
重入性是指任意現場在獲取到鎖之後再次獲取該鎖而不會被鎖阻塞,重入性的實現主要需要解決兩個問題。
- 線程再次獲取鎖。鎖需要去識別獲取鎖的線程是否爲當前佔據鎖的線程,如果是,則獲取鎖成功。
- 鎖的最終釋放。線程重複n次獲取了鎖,當第n次釋放鎖後,其他線程能夠獲取到鎖。
線程獲取鎖
以非公平性(默認)實現爲例,當線程通過ReentrantLock的lock()方法再次獲取鎖時,會調用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;
}
同步狀態爲0不難理解,說明目前沒有任何線程獲取到鎖。而當同步狀態不爲0時,通過判斷當前線程是否爲獲取鎖的線程來決定操作是否成功,如果是獲取鎖的線程再次請求,則將同步狀態的值進行增加並返回true,表示同步狀態成功。
注:setExclusiveOwnerThread(current)方式是設置AbstractOwnableSynchronizer的成員變量exclusiveOwnerThread,該變量表明獨佔式鎖的線程所有者。
鎖釋放
線程通過ReentrantLock的unlock()方法來釋放鎖,會調用tryRelease方法釋放同步狀態。
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;
}
從tryRelease方法中可以看到,同步狀態是否爲0是鎖是否最終被釋放的依據,當且僅當同步狀態爲0,將佔有線程設置爲null,並返回true,表示釋放成功。