6.類似Object監視器方法的Condition介面
在《1.有關線程、並發的基本概念》中,我們利用synchronized關鍵字、Queue隊列、以及Object監視器方法實現了生產者消費者,介紹了有關線程的一些基本概念。Object類提供的wait的方法和notifyAll方法,與之對應的是Condition介面提供是await和signalAll。await(或wait)是讓當前線程進入等待狀態並釋放鎖,signalAll(或notifyAll)則是喚醒等待中的線程,使得等待中的線程有競爭鎖的資格,注意只是資格,並不代表被喚醒的線程就一定會獲得鎖。
Condition介面的具體實現還是在AbstractQueuedSynchronizer中的內部實現的——AbstractQueuedSynchronizer$ConditionObject。ConditionObject中維護了一個「等待隊列」,注意這個和AQS同步器維護的「同步隊列」不同。AQS所維護的同步隊列是當前等待資源(同步狀態)的隊列,當前線程獲取同步狀態失敗時,同步器會將當前線程以及等待狀態等信息構造成一個節點並加入到同步隊列中,同時阻塞當前線程,當同步狀態被所持有的線程釋放時會將同步隊列中的首節點喚醒重新獲取同步狀態。而每個Condition維護一個等待隊列,該隊列的作用是一個等待signal信號的隊列。這兩者之間的關係是一個協同的關係,用下圖的說明它們之間的協同過程:
1. AQS的同步隊列如下圖所示,一個頭結點head指向隊首,一個tail指向隊尾,當線程調用lock方法獲取鎖而未成功時,線程被構造成節點加入到隊尾。(圖中NodeA是同步隊列的第一個節點,也就是獲得同步狀態的節點)
2.NodeA調用await方法時,NodeA從AQS同步隊列中移除,自然也就釋放了鎖,NodeA此時被加入到Condition的等待隊列中,等待signal信號,如下圖所示。
3.執行完第2步後,此時NodeB在同步隊列中處於第一個節點位置,即獲取到了鎖,如果NodeB此時執行signal(或者signalAll)方法,NodeA將會從Condition等待隊列中被移除即被喚醒,加入到同步隊列中,此時NodeA僅僅是被喚醒有了在同步隊列中爭奪資源的資格,並不代表被喚醒後就立即獲得鎖,如下圖所示。
4. 最後NodeB在signal執行完畢後,調用unLock方法釋放鎖,此時NodeA處於隊首,並爭奪同步狀態。
以上是AQS的「同步隊列」和Condition的「等待隊列」之間相互協作的過程,下面從源碼解析Condition的主要方法await、signal、signalAll。
1 public final void await throws InterruptedException{
2 if (Thread.interrupted) //線程被中斷則拋出中斷異常
3 throw new InterruptedException;
4 Node node = addConditionWaiter; //將線程構造為Node節點
5 long savedState = fullyRelease(node); //釋放鎖,返回同步狀態
6 int interruptMode = 0;
7 while (!isOnSyncQueue(node)) { //循環判斷當前節點是否在同步隊列中
8 LockSupport.park(this);
9 if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
10 break; //檢查節點在處於等待狀態時是否被中斷
11 }
12 //在跳出了循環,即被signal喚醒後重新加入了同步隊列後,開始重新競爭鎖
13 if (acquireQueued(node, savedState) && interruptMode != THROW_IE) //acquireQueued自旋獲取鎖,具體分析見《2.從AbstractQueuedSynchronizer(AQS)說起(1)——獨佔模式的鎖獲取與釋放》中對獲取同步狀態的解析 14 interruptMode = REINTERRUPT; 15 if (node.nextWaiter != null) 16 unlinkCancelledWaiters; //如果節點從等待狀態轉換為在同步隊列中,並且也已經獲得了鎖,此時將斷開此節點後面的等待節點 17 if (interruptMode != 0) 18 reportInterruptAfterWait(interruptMode); 19 }
在獲取鎖的線程調用await時,首先會將線程構造為Node節點並釋放鎖,此時線程被移出同步隊列加入到Condition等待隊列中,接著在第7行就會while循環判斷節點是否在同步隊列中,當沒有線程調用signal方法的時候顯然線程不在同步隊列,並將一直循環,直到有線程調用signal方法該線程才會被喚醒加入到同步隊列中,此時才會跳出循環。
signal和signalAll方法的異同在和notify和notifyAll一樣。signal只會喚醒等待隊列中位於隊首的節點使其具有競爭鎖的資格,而signalAll則會喚醒等待隊列中所有節點使所有節點都具有競爭鎖的資格。
1 public final void signal {
2 if (!isHeldExclusively) //判斷當前線程是否持有鎖
3 throw new IllegalMonitorStateException;
4 Node first = firstWaiter;
5 if (first != null)
6 doSignal(first); //喚醒等待隊列中的第一個節點
7 }
對比signalAll方法,不同點在於第6行是喚醒等待隊列中的所有節點——doSignalAll(first),不再貼出代碼。
private void doSignal(Node first) {
do {
if ((firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) && (first = firstWaiter) != null) //transferForSignal方法將處於等待隊列中的節點添加到同步隊列中
}
至於doSignalAll則是循環調用transferForSignal使得所有節點都被喚醒加入到同步隊列中。
當節點從等待隊列中加入到同步隊列中時,呼應await中的循環等待節點是否在同步隊列中,await和signal的協同配合也就很清晰明了了。
※hadoop源碼解析之hdfs內部結構分析
※對 Servlet 的改進——Struts2 引入
※03 編譯安裝apache的簡易配置
※圖像工具包VintaSoftImaging.NET SDK v8.5,新增獨立web服務
TAG:達人科技 |
※Use of the Apnea Monitor使用窒息監視器
※惡意廣告程序 RottenSys 感染近 500 萬台 Android 設備;市面上的智能設備=現成的「監視器」?
※談資 | ATOMOS 首款純監視器 Shinobi 開箱
※明星| 美國警方公布XXXTentacion遭遇搶劫時監視器畫面!
※IHS Markit:2018年144 Hz幀速率遊戲監視器面板趨勢
※EIZO發布全新專業級監視器:覆蓋99% Adobe RGB色域
※蘋果發布帶有電池健康監視器的iOS 11.3 beta 2,自帶有硬體修復
※主導次世代監視器標準,鴻海、Sony 等企業組 NICE 聯盟
※國產360掃地機器人Diqee被曝存在安全漏洞秒變監視器
※D-Link雲計算監視器有拍攝畫面可遭攔截及篡改固件安全漏洞
※用AI幫零售商減少盜損,NTT推出了一款AI監視器
※TCL VA43-L21 43寸商用液晶監視器報價5099元
※TCL VA43-L21 43寸商用液晶監視器報價5000元含稅
※主人買監視器看貓,結果反被貓監視....
※戴爾新推出的49英寸雙QHD曲面監視器可同時連接兩台PC
※美國國家安全局內部圖片,NSA官員辦公桌上7台監視器
※要價3750美元,RED發布2200尼特超亮監視器
※林志穎與陳若儀一起拍mv 一起看監視器畫面,網友:兩人好配啊!
※伴隨地球長達1.3萬年,科學家推測是衛星,或是外星的監視器
※佳能為4K專業監視器提供免費固件升級服務