Condition接口見Condition接口詳解
ConditionObject定義
- 定義為AQS的public內部類,方便獲取FIFO同步隊列,并將此類對象對外暴露.
- ** 條件等待隊列是單向隊列**:需要注意的是和AQS的FIFO的head相比,此首節點并沒有head節點信息傳播通知的功能,而且首節點是第一個阻塞的線程節點。
1- await方法
- await
步驟:
- 將節點加入條件等待隊列中
- 釋放同步狀態
- 死循環阻塞,直到被通知或者被中斷:1) 當被通知喚醒時還得判斷一下當前節點是否已經轉移到AQS同步隊列當中(其實主動通知的線程會確保其后繼等待節點轉移到同步隊列中,所以被通知后在下一次循環條件為false,繼續后續流程);2) 當被中斷喚醒時需要確保節點被轉移到同步隊列中,然后根據中斷發生在被通知前后位置設置中斷模式,并跳出循環。
- 關于中斷模式: 1) 當在被通知前被中斷則將中斷模式設置為THROW_IE; 2) 當在被通知后則將中斷模式設置為REINTERRUPT(因為acquireQueued不會響應中斷)。
- 死循環獲取同步狀態,并在同步狀態獲取成功或者取消獲取時設置中斷模式:如果在被通知之后獲取鎖過程中發生中斷則將中斷模式設置為REINTERRUPT。
- 刪除取消的后繼等待節點。
- 根據中斷模式拋出異常。
注意:被中斷的線程跳出while循環后,會調用acquireQueued方法自旋獲取鎖,嘗試獲取同步狀態,而不是立即響應中斷拋出中斷異常。在最后根據中斷模式來決定是否拋出異常。
- addConditionWaiter
調用await方法釋放鎖并將線程添加到條件等待隊列中并沒有采用死循環CAS設置(對比AQS.enq方法),因為Condition對象只能用于獨占模式,而且在調用await之前會顯示的獲取獨占鎖,否則會拋出非法監視器狀態異常。
- fullyRelease
等待的線程,是已經獲取到鎖的線程,當線程調用wait方法時會首先釋放鎖,然后再阻塞自自身。** 當沒有顯示的獲取鎖,直接調用await方法,會在這個方法拋出非法監視器異常的錯誤 **。
- isOnSyncQueue
- 第一個if語句:不管是因為中斷還是被通知(詳見transferAfterCancelledWait()和transferForSignal()方法)轉移到AQS同步隊列的節點狀態為都會設置為初始狀態(值為0),所以當發現
node.waitStatus == Node.CONDITION
為真時,說明還沒有轉移到同步隊列中,返回false,在下一次while循環中判斷是否轉移成功。 - 第二個if語句進行判斷,當節點是AQS同步隊列的中間節點時(在同步隊列中含有next節點)則返回true;
- 當節點為尾節點時,在return語句里 ,從后到前遍歷,如果存在則返回true,否則返回false。
- findNodeFromTail
- checkInterruptWhileWaiting
當發生中斷,則確保中斷的線程加入同步隊列中,并根據transferAfterCancelledWait的返回值來設置中斷模式。
- reportInterruptAfterWait
如果中斷模式為THROW_IE則拋出中斷異常
- unlinkCancelledWaiters
- transferAfterCancelledWait
確保取消的節點加入同步隊列中,如果中斷或者超時發生在通知之前則將狀態設置為0并返回true,否則返回false。
2- awaitUninterruptibly方法
3- awaitNanos方法
4- awaitUntil方法
5- await(long time, TimeUnit unit)方法
6- signal方法
每一個被通知的節點狀態由CONDITION設置為0,并隨后確保被通知的節點在加入到同步隊列后能被前繼節點通知到(SIGNAL或者直接喚醒)。
7- signalAll方法
查詢操作
8- isOwnedBy方法
9- hasWaiters方法
10- getWaitQueueLength方法
11- getWaitingThreads方法
總結
- 每一個創建的ConditionObject都維持這各自的一個單向的等待隊列,但是每個ConditionObject都共享一個AQS的FIFO同步隊列,當調用await方法時釋放鎖并進入阻塞狀態,調用signal方法將條件等待隊列中的首節點線程移動到AQS同步隊列中并將其前繼節點設置為SIGNAL或者直接喚醒線程使得被通知的線程能去獲取鎖。
- 調用await方法釋放鎖并將線程添加到條件等待隊列中并沒有采用死循環CAS設置(參考AQS.enq方法),因為Condition對象只能用于獨占模式,而且在調用await之前會顯示的獲取獨占鎖,否則會拋出非法監視器狀態異常。
- 調用signal方法將轉移等待節點,也不需要CAS來保證,因為signal會確保調用者caller是獲取獨占鎖的線程(通過isHeldExclusively方法來判斷,如果為false會拋出非法監視器狀態的異常)。