在Java concurrent包源碼走讀(二)我們知道AQS中有個(gè)條件隊(duì)列,但是具體它的作用是干什么愕撰、它和同步隊(duì)列有個(gè)關(guān)系搞挣,接下來(lái)這篇我們來(lái)了解AQS中的條件隊(duì)列。首先我們先看一下和條件隊(duì)列關(guān)聯(lián)的ReentrantLock類叽躯。
ReentrantLock
類圖
從類圖中我們可以看到此類實(shí)現(xiàn)Lock,同時(shí)有個(gè)抽象類Sync馋嗜,Sync這個(gè)類是繼承AbstractQueuedSynchronizer齐板,同時(shí)它也兩個(gè)子類,F(xiàn)airSync和NonfairSync葛菇。由此可見ReentrantLock它可以公平的獲取鎖也可以非公平方式獲取鎖甘磨。我們通過(guò)源碼還可以看出ReentrantLock特點(diǎn):
互斥鎖
支持公平和非公平獲取鎖,默認(rèn)是非公平
可重入鎖
支持條件變量(實(shí)現(xiàn)Lock接口)
對(duì)于ReentrantLock獲取釋放鎖的源碼我們就再分析眯停,感興趣的同學(xué)可以走讀济舆,主要看tryAcquire和tryRelease方法。走讀的時(shí)候可以帶著下面的兩個(gè)問(wèn)題莺债?
ReentrantLock如何實(shí)現(xiàn)可重入滋觉?
ReentrantLock的Lock為何要需要try catch,并且lock需要在try的外面齐邦?
條件隊(duì)列
我們主通過(guò)await和signal方法分析同步隊(duì)列和條件隊(duì)列的交互椎侠。
await()方法
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//將節(jié)點(diǎn)放入等待隊(duì)列
Node node = addConditionWaiter();
//釋放節(jié)點(diǎn)占的鎖
int savedState = fullyRelease(node);
int interruptMode = 0;
//輪詢判斷節(jié)點(diǎn)是否在AQS隊(duì)列
while (!isOnSyncQueue(node)) {
//如果在則阻塞節(jié)點(diǎn)對(duì)應(yīng)的線程
//它是何時(shí)加入到AQS隊(duì)列中呢?signal()
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
//喚醒后繼續(xù)競(jìng)爭(zhēng)鎖措拇,失敗后繼續(xù)阻塞
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
signal()方法
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
//喚醒等待隊(duì)列第一個(gè)節(jié)點(diǎn)我纪,注意只是喚醒,競(jìng)爭(zhēng)到鎖的看AQS隊(duì)列
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
通過(guò)上面源碼的分析丐吓,我們知道AQS自己維護(hù)的隊(duì)列是當(dāng)前等待資源的隊(duì)列浅悉,AQS會(huì)在資源被釋放后,依次喚醒隊(duì)列中從前到后的所有節(jié)點(diǎn)汰蜘,使他們對(duì)應(yīng)的線程恢復(fù)執(zhí)行仇冯。直到隊(duì)列為空。而條件隊(duì)列維護(hù)一個(gè)等待signal信號(hào)的隊(duì)列族操,兩個(gè)隊(duì)列的作用是不同苛坚,事實(shí)上比被,每個(gè)線程也僅僅會(huì)同時(shí)存在以上兩個(gè)隊(duì)列中的一個(gè)泼舱,流程是這樣的:
線程1調(diào)用reentrantLock.lock時(shí)等缀,線程被加入到AQS的等待隊(duì)列中娇昙。
線程1調(diào)用await方法被調(diào)用時(shí)尺迂,該線程從AQS中移除冒掌,對(duì)應(yīng)操作是鎖的釋放噪裕。
接著馬上被加入到Condition的等待隊(duì)列中,意味著該線程需要signal信號(hào)股毫。
線程2因?yàn)榫€程1釋放鎖的關(guān)系,被喚醒铃诬,并判斷可以獲取鎖,于是線程2獲取鎖趣席,并被加入到AQS的等待隊(duì)列中兵志。
線程2調(diào)用signal方法,這個(gè)時(shí)候Condition的等待隊(duì)列中只有線程1一個(gè)節(jié)點(diǎn)宣肚,于是它被取出來(lái)想罕,并被加入到AQS的等待隊(duì)列中。注意弧呐,這個(gè)時(shí)候線程1并沒有被喚醒
signal方法執(zhí)行完畢,線程2調(diào)用reentrantLock.unLock()方法俘枫,釋放鎖。這個(gè)時(shí)候因?yàn)锳QS中只有線程1鸠蚪,于是师溅,AQS釋放鎖后按從頭到尾的順序喚醒線程時(shí)茅信,線程1被喚醒墓臭,于是線程1回復(fù)執(zhí)行蘸鲸。
直到釋放所整個(gè)過(guò)程執(zhí)行完畢窿锉。
可以看到膝舅,整個(gè)協(xié)作過(guò)程是靠結(jié)點(diǎn)在AQS的等待隊(duì)列和條件隊(duì)列中來(lái)回移動(dòng)實(shí)現(xiàn)的窑多,條件隊(duì)列維護(hù)了一個(gè)等待信號(hào)的節(jié)點(diǎn)仍稀,并在適時(shí)的時(shí)候?qū)⒔Y(jié)點(diǎn)加入到AQS的等待隊(duì)列中來(lái)實(shí)現(xiàn)的喚醒操作埂息。