簡述
ReentrantLock是java中非常重要的一個并發(fā)工具趟妥,相比于java原生的synchronized有著更好的性能
## 概念速查
ReentrantLock涉及的名稱和概念較多仅颇,這里做一個簡單的歸類和解釋冯挎,具體更為詳細(xì)的內(nèi)容,請自行Baidu或Google寥院,這部分用于在閱讀文章的時候掰盘,快速了解一些名稱的概念,如果已經(jīng)熟悉矩肩,請?zhí)^现恼。
快速預(yù)覽
更強(qiáng)大的功能,玩玩意味著更為復(fù)雜的使用,ReentrankLock的使用比起synchronize叉袍,多了一個主動釋放鎖的代碼始锚,一個典型的使用示例如下
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
// ... method body
}finally {
lock.unlock();
}
注意unlock的操作一定要置于finally塊中,這樣才能保證鎖一定能釋放喳逛。
uml圖
看完了簡單的使用示例瞧捌,我們來快速的看一遍ReentrankLock的結(jié)構(gòu),下面是用idea的工具快速生成的uml圖艺配,感謝idea察郁,大大提高了我們的工作質(zhì)量。
一次性過于深入的討論转唉,往往會迷失在繁瑣的細(xì)節(jié)中皮钠,而難以把握全貌,而細(xì)節(jié)往往是由全局的目標(biāo)決定的赠法,所以我們一層一層的談麦轰,不一次性深入最終代碼。
由uml圖砖织,我們可以看出款侵,ReentrantLock類是一個Lock接口的具體實(shí)現(xiàn),每個ReentrantLock的實(shí)例侧纯,都持有一個sync對象新锈,且這個sync是final修飾的,這個sync有兩種具體的子類眶熬,分別是NonfairSync和FairSync妹笆,也就是非公平鎖和公平鎖。
ReentrantLock有兩個構(gòu)造方法娜氏,我們可以先看這兩個方法拳缠,
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = (fair)? new FairSync() : new NonfairSync();
}
可以看出,所謂構(gòu)造函數(shù)贸弥,其實(shí)就是初始化需要使用的sync的類型窟坐,默認(rèn)是非公平鎖。參考公平鎖與非公平鎖
簡要的預(yù)備知識
CAS
ReentrantLock的核心是對CAS方法的使用绵疲,現(xiàn)代的CPU提供了特殊的指令哲鸳,可以自動更新共享數(shù)據(jù),而且能夠檢測到其他線程的干擾盔憨,而 compareAndSet()使用了這些命令帕胆,具體請查閱CAS原理分析
wait
第一層解析
加鎖部分
ReentrantLock的lock其實(shí)就是調(diào)用具體的sync的lock方法。
公平鎖和非公平鎖的加鎖是有所不同的
對于公平鎖來說如下
final void lock() {
acquire(1);
}
對于非公平鎖來說般渡,是如下的
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else acquire(1);
}
很顯然懒豹,我們可以看出對于公平鎖來說芙盘,非公平鎖多了一個操作。我們首先來解釋一下compareAndSetState方法脸秽,這個方法來自于sync繼承的AbstractQueuedSynchronizer父類儒老。state為0表示當(dāng)前鎖沒有被占用,如果大于0记餐,則表示被持有了驮樊,使用compareAndSetState,底層是個CAS方法片酝,如果鎖沒有被占用囚衔,則置為占用狀態(tài),并且通過setExclusiveOwnerThread雕沿,將當(dāng)前線程設(shè)置為該鎖的持有者练湿。
tryAcquire這個方法的作用,java的官方注釋上如下寫到
Acquires in exclusive mode, ignoring interrupts
大意是"嘗試在獨(dú)占模式下獲取审轮,忽略中斷"肥哎。
截止到目前為止,我們看到的加鎖過程的流程圖如下疾渣,很簡單
acquire的工作
接下來篡诽,我們研究一下acquire做的工作
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
tryAcquire,嘗試將鎖的狀態(tài)置為arg榴捡,如果成功杈女,則結(jié)束function,如果失敗吊圾,在CHL隊列中添加一個新的獨(dú)占鎖的節(jié)點(diǎn)碧信,節(jié)點(diǎn)如果添加失敗粒氧,則不做后續(xù)處理歧杏,如果成功則使用selfInterrupt將當(dāng)前線程中斷祸轮。流程圖如下
解鎖部分
我們跳過acquire具體做了什么,我們直接來看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;
}