AQS的ConditionObject源碼詳解

Condition接口見Condition接口詳解

ConditionObject定義
  1. 定義為AQS的public內部類,方便獲取FIFO同步隊列,并將此類對象對外暴露.
  2. ** 條件等待隊列是單向隊列**:需要注意的是和AQS的FIFO的head相比,此首節點并沒有head節點信息傳播通知的功能,而且首節點是第一個阻塞的線程節點。
1- await方法
  1. await

步驟:

  1. 將節點加入條件等待隊列中
  2. 釋放同步狀態
  3. 死循環阻塞,直到被通知或者被中斷:1) 當被通知喚醒時還得判斷一下當前節點是否已經轉移到AQS同步隊列當中(其實主動通知的線程會確保其后繼等待節點轉移到同步隊列中,所以被通知后在下一次循環條件為false,繼續后續流程);2) 當被中斷喚醒時需要確保節點被轉移到同步隊列中,然后根據中斷發生在被通知前后位置設置中斷模式,并跳出循環。
  4. 關于中斷模式: 1) 當在被通知前被中斷則將中斷模式設置為THROW_IE; 2) 當在被通知后則將中斷模式設置為REINTERRUPT(因為acquireQueued不會響應中斷)。
  5. 死循環獲取同步狀態,并在同步狀態獲取成功或者取消獲取時設置中斷模式:如果在被通知之后獲取鎖過程中發生中斷則將中斷模式設置為REINTERRUPT。
  6. 刪除取消的后繼等待節點。
  7. 根據中斷模式拋出異常。

注意:被中斷的線程跳出while循環后,會調用acquireQueued方法自旋獲取鎖,嘗試獲取同步狀態,而不是立即響應中斷拋出中斷異常。在最后根據中斷模式來決定是否拋出異常。

  1. addConditionWaiter

調用await方法釋放鎖并將線程添加到條件等待隊列中并沒有采用死循環CAS設置(對比AQS.enq方法),因為Condition對象只能用于獨占模式,而且在調用await之前會顯示的獲取獨占鎖,否則會拋出非法監視器狀態異常。

  1. fullyRelease

等待的線程,是已經獲取到鎖的線程,當線程調用wait方法時會首先釋放鎖,然后再阻塞自自身。** 當沒有顯示的獲取鎖,直接調用await方法,會在這個方法拋出非法監視器異常的錯誤 **。

  1. isOnSyncQueue
  • 第一個if語句:不管是因為中斷還是被通知(詳見transferAfterCancelledWait()和transferForSignal()方法)轉移到AQS同步隊列的節點狀態為都會設置為初始狀態(值為0),所以當發現node.waitStatus == Node.CONDITION為真時,說明還沒有轉移到同步隊列中,返回false,在下一次while循環中判斷是否轉移成功。
  • 第二個if語句進行判斷,當節點是AQS同步隊列的中間節點時(在同步隊列中含有next節點)則返回true;
  • 當節點為尾節點時,在return語句里 ,從后到前遍歷,如果存在則返回true,否則返回false。
  1. findNodeFromTail
  1. checkInterruptWhileWaiting

當發生中斷,則確保中斷的線程加入同步隊列中,并根據transferAfterCancelledWait的返回值來設置中斷模式。

  1. reportInterruptAfterWait

如果中斷模式為THROW_IE則拋出中斷異常

  1. unlinkCancelledWaiters
  1. 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方法
總結
  1. 每一個創建的ConditionObject都維持這各自的一個單向的等待隊列,但是每個ConditionObject都共享一個AQS的FIFO同步隊列,當調用await方法時釋放鎖并進入阻塞狀態,調用signal方法將條件等待隊列中的首節點線程移動到AQS同步隊列中并將其前繼節點設置為SIGNAL或者直接喚醒線程使得被通知的線程能去獲取鎖。
  2. 調用await方法釋放鎖并將線程添加到條件等待隊列中并沒有采用死循環CAS設置(參考AQS.enq方法),因為Condition對象只能用于獨占模式,而且在調用await之前會顯示的獲取獨占鎖,否則會拋出非法監視器狀態異常。
  3. 調用signal方法將轉移等待節點,也不需要CAS來保證,因為signal會確保調用者caller是獲取獨占鎖的線程(通過isHeldExclusively方法來判斷,如果為false會拋出非法監視器狀態的異常)。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容