ReentrantLock源碼解析

看這部分的前提是大家已經(jīng)看過(guò)AbstractQueuedSynchronizer這個(gè)類尺棋,知道它是個(gè)啥了哈及塘,如果不知道莽使,請(qǐng)先看這里http://www.reibang.com/p/c629aebcd251锐极。

咳咳笙僚,這個(gè)鎖嘛,就是當(dāng)程序的某個(gè)部分被多個(gè)線程執(zhí)行時(shí)想讓線程挨個(gè)執(zhí)行它而不是大家一擁而上灵再,所以就用鎖來(lái)控制它肋层,這個(gè)鎖同時(shí)只能被一個(gè)線程占據(jù),這樣某些程序才不會(huì)因?yàn)椴l(fā)而出錯(cuò)翎迁。

先看怎么使用栋猖,再看實(shí)現(xiàn)原理吧。

public classReentrantTestextendsThread {

privateReentrantLockreentrantLock;

??? publicReentrantTest(ReentrantLock reentrantLock){

this.reentrantLock= reentrantLock;

???}

@Override

???public voidrun() {

//??????? reentrantLock.lock();

???????try{

for(intj =0;j <100000;j++) {

System.out.print(j);

???????????????System.out.print("\n");

???????????}

}finally{

//??????????? reentrantLock.unlock();

???????}

??? }

public static voidmain(String[] args) {

ReentrantLock reentrantLock =newReentrantLock();

???????ReentrantTest reentrantTest =newReentrantTest(reentrantLock);

???????ReentrantTest reentrantTest1 =newReentrantTest(reentrantLock);

???????reentrantTest.start();

???????reentrantTest1.start();

???}

}

這是一段簡(jiǎn)單的示例代碼汪榔,目的是為了讓每個(gè)線程可以完整打出0到99999蒲拉,不被中斷,有人會(huì)問(wèn)那你干嘛用多線程…..=痴腌。=我承認(rèn)我例子想得不好雌团,反正就這樣!大家試著跑一跑就能看到效果啦士聪,如果注釋掉上面那兩行锦援,程序輸出的結(jié)果就是混亂的,兩個(gè)線程輸出的結(jié)果是交雜的剥悟,但是用了lock就會(huì)是一個(gè)線程執(zhí)行完才下個(gè)線程灵寺。

這里又有人問(wèn)了,那這個(gè)跟在方法前加個(gè)synchronise有啥區(qū)別区岗?

首先略板,這都JDK10都要出來(lái)了,synchronise在進(jìn)行了一系列優(yōu)化后慈缔,性能還算OK叮称,跟ReentrantLock差距不大了。既然性能差的不多,那就從功能上來(lái)對(duì)比颅拦,確實(shí)synchronise很方便蒂誉,一切由jvm控制,但是它的粒度不如ReentrantLock細(xì)距帅,怎么說(shuō)呢右锨,ReentrantLock是要我們自己寫,從哪里lock碌秸,所以就算在一個(gè)方法內(nèi)部绍移,它也可以控制方法的一部分是受到鎖的控制,而另一部分不受讥电,所以粒度比較細(xì)蹂窖,但要注意一定要在finally中釋放鎖,不然程序執(zhí)行出現(xiàn)異常等提前退出的現(xiàn)象可能會(huì)導(dǎo)致死鎖恩敌。

好瞬测,然后呢,ReentrantLock提供了一些額外的功能纠炮,比如可重入月趟,可中斷,可限時(shí)恢口,可公平等孝宗,下面在講它實(shí)現(xiàn)的時(shí)候會(huì)挨個(gè)講,這些都是synchronise不具備的啦耕肩。

ReentrantLock名字叫Reentrant對(duì)吧因妇,重新進(jìn)入的意思,相信大家聽(tīng)說(shuō)過(guò)可重入鎖猿诸,這其實(shí)跟這個(gè)ReentrantLock的實(shí)現(xiàn)機(jī)制有關(guān)婚被,它內(nèi)部定義了一個(gè)私有類Sync(這個(gè)就是實(shí)現(xiàn)鎖這個(gè)功能的關(guān)鍵):

abstractstaticclassSyncextendsAbstractQueuedSynchronizer {

//序列號(hào)

privatestaticfinallongserialVersionUID = -5179523762034025860L;

//獲取鎖

abstractvoidlock();

//非公平方式獲取

finalbooleannonfairTryAcquire(intacquires) {

//當(dāng)前線程

finalThread current =Thread.currentThread();

//獲取狀態(tài)

intc =getState();

if(c == 0) {//表示沒(méi)有線程正在競(jìng)爭(zhēng)該鎖

if(compareAndSetState(0, acquires)) {//比較并設(shè)置狀態(tài)成功,狀態(tài)0表示鎖沒(méi)有被占用

//設(shè)置當(dāng)前線程獨(dú)占setExclusiveOwnerThread(current);

returntrue;//成功}

}

elseif(current == getExclusiveOwnerThread()) {//當(dāng)前線程擁有該鎖

intnextc = c + acquires;//增加重入次數(shù)

if(nextc < 0)//overflow

thrownewError("Maximum lock count exceeded");

//設(shè)置狀態(tài)setState(nextc);

//成功

returntrue;

}

//失敗

returnfalse;

}

//試圖在共享模式下獲取對(duì)象狀態(tài)两芳,此方法應(yīng)該查詢是否允許它在共享模式下獲取對(duì)象狀態(tài)摔寨,如果允許,則獲取它

protectedfinalbooleantryRelease(intreleases) {

intc = getState() -releases;

if(Thread.currentThread() != getExclusiveOwnerThread())//當(dāng)前線程不為獨(dú)占線程

thrownewIllegalMonitorStateException();//拋出異常

//釋放標(biāo)識(shí)

booleanfree =false;

if(c == 0) {

free=true;

//已經(jīng)釋放怖辆,清空獨(dú)占

setExclusiveOwnerThread(null);

}

//設(shè)置標(biāo)識(shí)setState(c);

returnfree;

}

//判斷資源是否被當(dāng)前線程占有

protectedfinalbooleanisHeldExclusively() {

//While we must in general read state before owner,

//we don't need to do so to check if current thread is owner

returngetExclusiveOwnerThread() ==Thread.currentThread();

}

//新生一個(gè)條件

finalConditionObject newCondition() {

returnnewConditionObject();

}

//Methods relayed from outer class

//返回資源的占用線程

finalThread getOwner() {

returngetState() == 0 ?null: getExclusiveOwnerThread();

}

//返回狀態(tài)

finalintgetHoldCount() {

returnisHeldExclusively() ? getState() : 0;

}

//資源是否被占用

finalbooleanisLocked() {

returngetState() != 0;

}

/**

* Reconstitutes the instance from a stream (that is, deserializes it).

*/

//自定義反序列化邏輯

privatevoidreadObject(java.io.ObjectInputStream s)

throwsjava.io.IOException, ClassNotFoundException {

s.defaultReadObject();

setState(0);//reset to unlocked state}

}

好的是复,貼了一段代碼,就是Sync的定義竖螃,注意到淑廊,它繼承自AbstractQueuedSynchronizer,我講這個(gè)是為了講ReentrantLock的實(shí)現(xiàn)機(jī)制哈特咆,你打開(kāi)源碼就知道這個(gè)ReentrantLock其實(shí)沒(méi)別的東西季惩,主要就是靠這個(gè)Sync录粱,所以得把這個(gè)Sync是干嘛的講通。好的繼續(xù)画拾,這個(gè)Sync里就有幾個(gè)起主要作用的方法啥繁,下面一一說(shuō)明這個(gè)東西是怎么工作的。

首先青抛,最重要的方法:lock():

abstract void lock();

第一部分?FairSync

它是一個(gè)抽象方法旗闽,具體的實(shí)現(xiàn)在它FairSync和NonfairSync兩個(gè)子類中(一個(gè)是公平鎖一個(gè)是不公平鎖

),當(dāng)這個(gè)方法被調(diào)用時(shí)蜜另,意味著執(zhí)行該方法的當(dāng)前線程是在試圖去獲取這個(gè)鎖适室,我們先看NonfairSync的lock方法:

final voidlock() {

???if(compareAndSetState(0,1))

??????? setExclusiveOwnerThread(Thread.currentThread());

??? else

???????acquire(1);

}

可以看到compareAndSetState(0, 1)如果此刻鎖的狀態(tài)是0就代表了鎖現(xiàn)在沒(méi)人占,那么我們當(dāng)前線程直接占了就行举瑰,否則捣辆,執(zhí)行acquire方法:

public final voidacquire(intarg) {

???if(!tryAcquire(arg) &&

??????? acquireQueued(addWaiter(Node.EXCLUSIVE),arg))

???????selfInterrupt();

}

這個(gè)方法在AbstractQueuedSynchronizer里面哈,讓你們?nèi)タ戳舜搜福驗(yàn)樗腔A(chǔ)汽畴,然后這個(gè)方法里呢,tryAcquire則是在NonfairSync里實(shí)現(xiàn)的:

protected final booleantryAcquire(intacquires) {

???returnnonfairTryAcquire(acquires);

}

原來(lái)它也就調(diào)用的父類中的nonfairTryAcquire方法:

final booleannonfairTryAcquire(intacquires) {

???finalThread current = Thread.currentThread();

??? intc = getState();

??? if(c ==0) {

???????if(compareAndSetState(0,acquires)) {

??????????? setExclusiveOwnerThread(current);

??????????? return true;

???????}

??? }

???else if(current == getExclusiveOwnerThread()) {

???????intnextc = c + acquires;

??????? if(nextc <0)// overflow

???????????throw newError("Maximum lock count exceeded");

???????setState(nextc);

??????? return true;

???}

???return false;

}

分析一波邮屁,這個(gè)參數(shù)代表可以重復(fù)的獲取這個(gè)鎖整袁,State這個(gè)變量,存儲(chǔ)的就是這個(gè)鎖被占有的狀態(tài)佑吝,0就是沒(méi)被占有,1就是被占有一次绳匀,n就是被占有n次(這個(gè)次數(shù)可能描述不是特別恰當(dāng)芋忿,自己體會(huì)~),然后加入狀態(tài)是0疾棵,那么把這個(gè)state設(shè)置為acquires這么大戈钢,然后占有這個(gè)鎖,假如狀態(tài)不是0是尔,那么又會(huì)去判斷殉了,占有這個(gè)鎖的是不是當(dāng)前線程(不是多此一舉哈,因?yàn)榍懊嬲f(shuō)了拟枚,ReentrantLock可以重復(fù)申請(qǐng)薪铜,也就是可重入,那么就有可能這個(gè)鎖已經(jīng)被當(dāng)前線程占有啦)恩溅,那就把狀態(tài)的值再設(shè)為當(dāng)前的狀態(tài)值加上acquires隔箍,如果不滿足以上條件,啥都不干脚乡,返回false(返回false后接下來(lái)的操作就會(huì)回到AbstractQueuedSynchronizer里面去蜒滩,也就是排隊(duì)等待的那套流程,你們要看!8┘琛)

第二部分 NonFairSync

FairSync里面的lock方法更簡(jiǎn)單:

final voidlock() {

??? acquire(1);

}

直接執(zhí)行acquire方法捡遍,我們上面寫出來(lái)這個(gè)方法,所以還是會(huì)調(diào)用tryAcquire方法:

???protected final booleantryAcquire(intacquires) {

???????finalThread current = Thread.currentThread();

??????? intc = getState();

??????? if(c ==0) {

???????????if(!hasQueuedPredecessors() &&

??????????????? compareAndSetState(0,acquires)) {

??????????????? setExclusiveOwnerThread(current);

??????????????? return true;

???????????}

??????? }

???????else if(current == getExclusiveOwnerThread()) {

???????????intnextc = c + acquires;

??????????? if(nextc <0)

???????????????throw newError("Maximum lock count exceeded");

???????????setState(nextc);

??????????? return true;

???????}

???????return false;

???}

}

就是當(dāng)鎖狀態(tài)為0時(shí)竹握,公平鎖會(huì)調(diào)用hasQueuedPredecessors判斷是否有其他線程比它等待更長(zhǎng)時(shí)間稽莉,方法如下:? ?

public final booleanhasQueuedPredecessors() {

???Node t =tail;?

???Node h =head;

???Node s;

??? returnh != t &&

??????? ((s = h.next) ==null|| s.thread!= Thread.currentThread());

}

來(lái)看下它的判斷條件,?滿足下面幾個(gè)條件返回true:

首先等待隊(duì)列的頭節(jié)點(diǎn)不等于尾節(jié)點(diǎn),其次涩搓,頭節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)不為空污秆,或者當(dāng)前線程不是頭節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)。

如果返回true代表當(dāng)前線程節(jié)點(diǎn)前面還有等待的其他線程昧甘,那么為了公平當(dāng)前線程就不會(huì)去占有這個(gè)鎖(就是說(shuō)即使這個(gè)鎖現(xiàn)在是空著的也不會(huì)去占)良拼,不像不公平鎖,它不會(huì)去判斷是否隊(duì)列還有更靠前的線程在等待

???????else if(current == getExclusiveOwnerThread()) {

???????????intnextc = c + acquires;

??????????? if(nextc <0)

???????????????throw newError("Maximum lock count exceeded");

???????????setState(nextc);

??????????? return true;

???????}

這部分跟不公平鎖是一樣的充边。

對(duì)庸推,很簡(jiǎn)單就講完了,自然它里面還提供了一些其他方法浇冰,不過(guò)都很簡(jiǎn)單啦贬媒,大家可以自己去看,至于unlock:

public voidunlock() {

???sync.release(1);

}

簡(jiǎn)單哈肘习,就是調(diào)用:

public final booleanrelease(intarg) {

???if(tryRelease(arg)) {

??????? Node h =head;

??????? if(h !=null&& h.waitStatus!=0)

??????????? unparkSuccessor(h);

??????? return true;

???}

???return false;

}

先調(diào)用tryRlease方法:

protected final booleantryRelease(intreleases) {

???intc = getState() - releases;

??? if(Thread.currentThread() != getExclusiveOwnerThread())

???????throw newIllegalMonitorStateException();

??? booleanfree =false;

??? if(c ==0) {

??????? free =true;

???????setExclusiveOwnerThread(null);

???}

??? setState(c);

??? returnfree;

}

將狀態(tài)設(shè)為當(dāng)前狀態(tài)減去releases的值际乘,如果當(dāng)前獨(dú)占鎖的線程不是當(dāng)前線程,那么一定有毛病漂佩,拋異常脖含!否則,如果減去releases之后的狀態(tài)值為0了的話投蝉,代表該鎖已經(jīng)被完全釋放了养葵,那么就把這個(gè)鎖的所有者設(shè)置為null(不再是當(dāng)前線程)。

在tryRlease成功的情況下瘩缆,如果等待隊(duì)列的頭既不為null并且它的等待狀態(tài)不是0关拒,那么就要調(diào)用AQS中的unparkSuccessor方法(可能會(huì)喚醒它的下一個(gè)節(jié)點(diǎn)),這個(gè)等待狀態(tài)的說(shuō)明在AQS那篇文章里也有講哈庸娱,這里不重復(fù)了着绊。

整個(gè)獲取鎖和釋放鎖的過(guò)程就是這樣了。

第三部分 可限時(shí)

可限時(shí)和可中斷我們還沒(méi)講涌韩,我相信可限時(shí)很好理解畔柔,就是獲取鎖的時(shí)候等待特定的時(shí)間后就不再等待直接返回蔑穴,相應(yīng)的方法:

public booleantryLock(longtimeout,TimeUnit unit)

???????throwsInterruptedException {

???returnsync.tryAcquireNanos(1,unit.toNanos(timeout));

}

第四部分 可中斷

可中斷有什么用呢渐白?就是它能夠及時(shí)地響應(yīng)中斷信號(hào)(可能從別的線程發(fā)送過(guò)來(lái)的中斷信號(hào))叨粘,ReentrantLock里面的可中斷方法是:

public voidlockInterruptibly()throwsInterruptedException {

???sync.acquireInterruptibly(1);

}

走進(jìn)acquireInterruptibly方法:

public final voidacquireInterruptibly(intarg)

???????throwsInterruptedException {

???if(Thread.interrupted())

???????throw newInterruptedException();

??? if(!tryAcquire(arg))

??????? doAcquireInterruptibly(arg);

}

其實(shí)就是判斷當(dāng)前的Thread的中斷信號(hào),如果是被中斷的狀態(tài)就直接拋異常诚欠,就不會(huì)繼續(xù)去tryAcquire了吧碾,否則還是會(huì)去等待獲取鎖睦刃,跟lock()一樣不瓶。

看看doAcquireInterruptibly方法:

private voiddoAcquireInterruptibly(intarg)

throwsInterruptedException {

finalNode node = addWaiter(Node.EXCLUSIVE);

??? booleanfailed =true;

??? try{

for(;;) {

finalNode p = node.predecessor();

??????????? if(p ==head&& tryAcquire(arg)) {

setHead(node);

???????????????p.next=null;// help GC

???????????????failed =false;

??????????????? return;

???????????}

if(shouldParkAfterFailedAcquire(p,node) &&

??????????????? parkAndCheckInterrupt())

throw newInterruptedException();

???????}

}finally{

if(failed)

cancelAcquire(node);

???}

}

在我那篇AbstractQueuedSynchronizer源碼解析講過(guò)acquireQueued方法:

final booleanacquireQueued(finalNode node, intarg) {

booleanfailed =true;

??? try{

booleaninterrupted =false;

??????? for(;;) {

finalNode p = node.predecessor();

??????????? if(p ==head&& tryAcquire(arg)) {

setHead(node);

???????????????p.next=null;// help GC

???????????????failed =false;

??????????????? returninterrupted;

???????????}

if(shouldParkAfterFailedAcquire(p,node) &&

??????????????? parkAndCheckInterrupt())

interrupted =true;

???????}

}finally{

if(failed)

cancelAcquire(node);

???}

}

仔細(xì)對(duì)比一下它們的區(qū)別,就只是當(dāng)線程中斷標(biāo)志為true的時(shí)候枚粘,acquireQueued只是保存一下當(dāng)前線程的狀態(tài)(傳遞出去以免被重置了)馅闽,而doAcquireInterruptibly直接就拋異常了。

那么中斷的好處就是馍迄,當(dāng)我們定義線程需要被中斷的時(shí)候就直接異常福也,返回,不再繼續(xù)執(zhí)行下面的操作攀圈,節(jié)約了時(shí)間暴凑,對(duì)中斷不了解的請(qǐng)看http://www.reibang.com/p/5708ac61226b

ReentrantLock也就差不多了~寫的有什么不對(duì)的地方歡迎指正。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末赘来,一起剝皮案震驚了整個(gè)濱河市现喳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌犬辰,老刑警劉巖嗦篱,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異幌缝,居然都是意外死亡灸促,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門狮腿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)腿宰,“玉大人,你說(shuō)我怎么就攤上這事缘厢。” “怎么了甩挫?”我有些...
    開(kāi)封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵贴硫,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我伊者,道長(zhǎng)英遭,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任亦渗,我火速辦了婚禮挖诸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘法精。我一直安慰自己多律,他們只是感情好痴突,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著狼荞,像睡著了一般辽装。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上相味,一...
    開(kāi)封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天拾积,我揣著相機(jī)與錄音,去河邊找鬼丰涉。 笑死拓巧,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的一死。 我是一名探鬼主播肛度,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼摘符!你這毒婦竟也來(lái)了贤斜?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤逛裤,失蹤者是張志新(化名)和其女友劉穎瘩绒,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體带族,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡锁荔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蝙砌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片阳堕。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖择克,靈堂內(nèi)的尸體忽然破棺而出恬总,到底是詐尸還是另有隱情,我是刑警寧澤肚邢,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布壹堰,位于F島的核電站,受9級(jí)特大地震影響骡湖,放射性物質(zhì)發(fā)生泄漏贱纠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一响蕴、第九天 我趴在偏房一處隱蔽的房頂上張望谆焊。 院中可真熱鬧,春花似錦浦夷、人聲如沸辖试。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)剃执。三九已至誓禁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間肾档,已是汗流浹背摹恰。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留怒见,地道東北人俗慈。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像遣耍,于是被迫代替她去往敵國(guó)和親闺阱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容