在《基于線程、并發(fā)的基本概念(詳解)》中,我們利用synchronized關(guān)鍵字、Queue隊(duì)列、以及Object監(jiān)視器方法實(shí)現(xiàn)了生產(chǎn)者消費(fèi)者,介紹了有關(guān)線程的一些基本概念。Object類提供的wait的方法和notifyAll方法,與之對(duì)應(yīng)的是Condition接口提供是await和signalAll。await(或wait)是讓當(dāng)前線程進(jìn)入等待狀態(tài)并釋放鎖,signalAll(或notifyAll)則是喚醒等待中的線程,使得等待中的線程有競(jìng)爭(zhēng)鎖的資格,注意只是資格,并不代表被喚醒的線程就一定會(huì)獲得鎖。
Condition接口的具體實(shí)現(xiàn)還是在AbstractQueuedSynchronizer中的內(nèi)部實(shí)現(xiàn)的——AbstractQueuedSynchronizer$ConditionObject。ConditionObject中維護(hù)了一個(gè)“等待隊(duì)列”,注意這個(gè)和AQS同步器維護(hù)的“同步隊(duì)列”不同。AQS所維護(hù)的同步隊(duì)列是當(dāng)前等待資源(同步狀態(tài))的隊(duì)列,當(dāng)前線程獲取同步狀態(tài)失敗時(shí),同步器會(huì)將當(dāng)前線程以及等待狀態(tài)等信息構(gòu)造成一個(gè)節(jié)點(diǎn)并加入到同步隊(duì)列中,同時(shí)阻塞當(dāng)前線程,當(dāng)同步狀態(tài)被所持有的線程釋放時(shí)會(huì)將同步隊(duì)列中的首節(jié)點(diǎn)喚醒重新獲取同步狀態(tài)。而每個(gè)Condition維護(hù)一個(gè)等待隊(duì)列,該隊(duì)列的作用是一個(gè)等待signal信號(hào)的隊(duì)列。這兩者之間的關(guān)系是一個(gè)協(xié)同的關(guān)系,用下圖的說(shuō)明它們之間的協(xié)同過(guò)程:
1. AQS的同步隊(duì)列如下圖所示,一個(gè)頭結(jié)點(diǎn)head指向隊(duì)首,一個(gè)tail指向隊(duì)尾,當(dāng)線程調(diào)用lock()方法獲取鎖而未成功時(shí),線程被構(gòu)造成節(jié)點(diǎn)加入到隊(duì)尾。(圖中NodeA是同步隊(duì)列的第一個(gè)節(jié)點(diǎn),也就是獲得同步狀態(tài)的節(jié)點(diǎn))
2.NodeA調(diào)用await()方法時(shí),NodeA從AQS同步隊(duì)列中移除,自然也就釋放了鎖,NodeA此時(shí)被加入到Condition的等待隊(duì)列中,等待signal信號(hào),如下圖所示。
3.執(zhí)行完第2步后,此時(shí)NodeB在同步隊(duì)列中處于第一個(gè)節(jié)點(diǎn)位置,即獲取到了鎖,如果NodeB此時(shí)執(zhí)行signal(或者signalAll)方法,NodeA將會(huì)從Condition等待隊(duì)列中被移除即被喚醒,加入到同步隊(duì)列中,此時(shí)NodeA僅僅是被喚醒有了在同步隊(duì)列中爭(zhēng)奪資源的資格,并不代表被喚醒后就立即獲得鎖,如下圖所示。
4. 最后NodeB在signal執(zhí)行完畢后,調(diào)用unLock方法釋放鎖,此時(shí)NodeA處于隊(duì)首,并爭(zhēng)奪同步狀態(tài)。
以上是AQS的“同步隊(duì)列”和Condition的“等待隊(duì)列”之間相互協(xié)作的過(guò)程,下面從源碼解析Condition的主要方法await、signal、signalAll。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public final void await() throws InterruptedException{ if (Thread.interrupted()) //線程被中斷則拋出中斷異常 throw new InterruptedException(); Node node = addConditionWaiter(); //將線程構(gòu)造為Node節(jié)點(diǎn) long savedState = fullyRelease(node); //釋放鎖,返回同步狀態(tài) int interruptMode = 0 ; while (!isOnSyncQueue(node)) { //循環(huán)判斷當(dāng)前節(jié)點(diǎn)是否在同步隊(duì)列中 LockSupport.park( this ); if ((interruptMode = checkInterruptWhileWaiting(node)) != 0 ) break ; //檢查節(jié)點(diǎn)在處于等待狀態(tài)時(shí)是否被中斷 } //在跳出了循環(huán),即被signal喚醒后重新加入了同步隊(duì)列后,開始重新競(jìng)爭(zhēng)鎖 if (acquireQueued(node, savedState) && interruptMode != THROW_IE) //acquireQueued自旋獲取鎖,具體分析見《2.從AbstractQueuedSynchronizer(AQS)說(shuō)起(1)——獨(dú)占模式的鎖獲取與釋放》中對(duì)獲取同步狀態(tài)的解析 interruptMode = REINTERRUPT; if (node.nextWaiter != null ) unlinkCancelledWaiters(); //如果節(jié)點(diǎn)從等待狀態(tài)轉(zhuǎn)換為在同步隊(duì)列中,并且也已經(jīng)獲得了鎖,此時(shí)將斷開此節(jié)點(diǎn)后面的等待節(jié)點(diǎn) if (interruptMode != 0 ) reportInterruptAfterWait(interruptMode); } |
在獲取鎖的線程調(diào)用await時(shí),首先會(huì)將線程構(gòu)造為Node節(jié)點(diǎn)并釋放鎖,此時(shí)線程被移出同步隊(duì)列加入到Condition等待隊(duì)列中,接著在第7行就會(huì)while循環(huán)判斷節(jié)點(diǎn)是否在同步隊(duì)列中,當(dāng)沒有線程調(diào)用signal方法的時(shí)候顯然線程不在同步隊(duì)列,并將一直循環(huán),直到有線程調(diào)用signal方法該線程才會(huì)被喚醒加入到同步隊(duì)列中,此時(shí)才會(huì)跳出循環(huán)。
signal和signalAll方法的異同在和notify和notifyAll一樣。signal只會(huì)喚醒等待隊(duì)列中位于隊(duì)首的節(jié)點(diǎn)使其具有競(jìng)爭(zhēng)鎖的資格,而signalAll則會(huì)喚醒等待隊(duì)列中所有節(jié)點(diǎn)使所有節(jié)點(diǎn)都具有競(jìng)爭(zhēng)鎖的資格。
1
2
3
4
5
6
7
|
public final void signal() { if (!isHeldExclusively()) //判斷當(dāng)前線程是否持有鎖 throw new IllegalMonitorStateException(); Node first = firstWaiter; if (first != null ) doSignal(first); //喚醒等待隊(duì)列中的第一個(gè)節(jié)點(diǎn) } |
對(duì)比signalAll方法,不同點(diǎn)在于第6行是喚醒等待隊(duì)列中的所有節(jié)點(diǎn)——doSignalAll(first),不再貼出代碼。
1
2
3
4
5
6
7
|
private void doSignal(Node first) { do { if ((firstWaiter = first.nextWaiter) == null ) lastWaiter = null ; first.nextWaiter = null ; } while (!transferForSignal(first) && (first = firstWaiter) != null ) //transferForSignal方法將處于等待隊(duì)列中的節(jié)點(diǎn)添加到同步隊(duì)列中 } |
至于doSignalAll則是循環(huán)調(diào)用transferForSignal使得所有節(jié)點(diǎn)都被喚醒加入到同步隊(duì)列中。
當(dāng)節(jié)點(diǎn)從等待隊(duì)列中加入到同步隊(duì)列中時(shí),呼應(yīng)await中的循環(huán)等待節(jié)點(diǎn)是否在同步隊(duì)列中,await和signal的協(xié)同配合也就很清晰明了了。
以上這篇類似Object監(jiān)視器方法的Condition接口(詳解)就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持服務(wù)器之家。