簡(jiǎn)述
ReentrantLock是java中非常重要的一個(gè)并發(fā)工具,相比于java原生的synchronized有著更好的性能
## 概念速查
ReentrantLock涉及的名稱(chēng)和概念較多,這里做一個(gè)簡(jiǎn)單的歸類(lèi)和解釋?zhuān)唧w更為詳細(xì)的內(nèi)容,請(qǐng)自行Baidu或Google,這部分用于在閱讀文章的時(shí)候,快速了解一些名稱(chēng)的概念,如果已經(jīng)熟悉,請(qǐng)?zhí)^(guò)。
快速預(yù)覽
更強(qiáng)大的功能,玩玩意味著更為復(fù)雜的使用,ReentrankLock的使用比起synchronize,多了一個(gè)主動(dòng)釋放鎖的代碼,一個(gè)典型的使用示例如下
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
// ... method body
}finally {
lock.unlock();
}
注意unlock的操作一定要置于finally塊中,這樣才能保證鎖一定能釋放。
uml圖
看完了簡(jiǎn)單的使用示例,我們來(lái)快速的看一遍ReentrankLock的結(jié)構(gòu),下面是用idea的工具快速生成的uml圖,感謝idea,大大提高了我們的工作質(zhì)量。

一次性過(guò)于深入的討論,往往會(huì)迷失在繁瑣的細(xì)節(jié)中,而難以把握全貌,而細(xì)節(jié)往往是由全局的目標(biāo)決定的,所以我們一層一層的談,不一次性深入最終代碼。
由uml圖,我們可以看出,ReentrantLock類(lèi)是一個(gè)Lock接口的具體實(shí)現(xiàn),每個(gè)ReentrantLock的實(shí)例,都持有一個(gè)sync對(duì)象,且這個(gè)sync是final修飾的,這個(gè)sync有兩種具體的子類(lèi),分別是NonfairSync和FairSync,也就是非公平鎖和公平鎖。
ReentrantLock有兩個(gè)構(gòu)造方法,我們可以先看這兩個(gè)方法,
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = (fair)? new FairSync() : new NonfairSync();
}
可以看出,所謂構(gòu)造函數(shù),其實(shí)就是初始化需要使用的sync的類(lèi)型,默認(rèn)是非公平鎖。參考公平鎖與非公平鎖
簡(jiǎn)要的預(yù)備知識(shí)
CAS
ReentrantLock的核心是對(duì)CAS方法的使用,現(xiàn)代的CPU提供了特殊的指令,可以自動(dòng)更新共享數(shù)據(jù),而且能夠檢測(cè)到其他線(xiàn)程的干擾,而 compareAndSet()使用了這些命令,具體請(qǐng)查閱CAS原理分析
wait
第一層解析
加鎖部分
ReentrantLock的lock其實(shí)就是調(diào)用具體的sync的lock方法。
公平鎖和非公平鎖的加鎖是有所不同的
對(duì)于公平鎖來(lái)說(shuō)如下
final void lock() {
acquire(1);
}
對(duì)于非公平鎖來(lái)說(shuō),是如下的
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else acquire(1);
}
很顯然,我們可以看出對(duì)于公平鎖來(lái)說(shuō),非公平鎖多了一個(gè)操作。我們首先來(lái)解釋一下compareAndSetState方法,這個(gè)方法來(lái)自于sync繼承的AbstractQueuedSynchronizer父類(lèi)。state為0表示當(dāng)前鎖沒(méi)有被占用,如果大于0,則表示被持有了,使用compareAndSetState,底層是個(gè)CAS方法,如果鎖沒(méi)有被占用,則置為占用狀態(tài),并且通過(guò)setExclusiveOwnerThread,將當(dāng)前線(xiàn)程設(shè)置為該鎖的持有者。
tryAcquire這個(gè)方法的作用,java的官方注釋上如下寫(xiě)到
Acquires in exclusive mode, ignoring interrupts
大意是"嘗試在獨(dú)占模式下獲取,忽略中斷"。
截止到目前為止,我們看到的加鎖過(guò)程的流程圖如下,很簡(jiǎn)單

acquire的工作
接下來(lái),我們研究一下acquire做的工作
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
tryAcquire,嘗試將鎖的狀態(tài)置為arg,如果成功,則結(jié)束function,如果失敗,在CHL隊(duì)列中添加一個(gè)新的獨(dú)占鎖的節(jié)點(diǎn),節(jié)點(diǎn)如果添加失敗,則不做后續(xù)處理,如果成功則使用selfInterrupt將當(dāng)前線(xiàn)程中斷。流程圖如下
解鎖部分
我們跳過(guò)acquire具體做了什么,我們直接來(lái)看unlock做了什么。
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}