【建議收藏】Java併發之AQS與ConditionObject

 AQS大體結構圖

a937b104da67e211797e4af9be7b8e62.png

  • 前面講解的可重入鎖和可重入讀寫鎖都是圍繞着阻塞隊列講解的。
  • 沒有提到AQS中的另外一個重要內容:等待隊列,也稱之爲條件(condition)隊列。
  • AQS有Node對象,其有兩個用途:造成等待隊列和阻塞隊列。
  • 雖然是Node,挺像鏈表的,可是jdk的註釋中只用了queues這個單詞,因此都稱之爲隊列,畢竟它傾向於FIFO。

以ReentrantLock中的newCondition爲例

不厭其煩的閱讀jdk源碼c++

newCondition()方法

009ec1e76179a5844b88d86161d83e3f.png

關於AQS中的Node的狀態

36c15f5fe359c6784342d5adc104fdf5.png

  • 狀態字段,僅取值: SIGNAL:此節點的後繼節點被(或即將)阻塞(經過park),所以當前節點在釋放或取消時必須解除其後繼節點的park。 爲了不競爭,獲取方法必須首先代表它們須要一個信號(signal),而後重試原子獲取,而後在失敗時阻塞。 CANCELLED:因爲超時或中斷,該節點被取消。 節點永遠不會離開這個狀態。 特別是,取消節點的線程永遠不會再次阻塞。 CONDITION:該節點當前在條件隊列中。 它在傳輸以前不會用做同步隊列節點,此時狀態將設置爲 0。(此處使用此值與該字段的其餘用途無關,但簡化了機制。) PROPAGATE:A releaseShared 應該傳播到其餘節點。 這在 doReleaseShared 中設置(僅適用於頭節點)以確保傳播繼續,即便其餘操做已經介入。 0:以上都不是 爲了簡化使用,數值按數字排列。 非負值意味着節點不須要發出信號,通知後繼節點。 所以,大多數代碼不須要檢查特定值,只需檢查符號。 對於普通同步節點,該字段被初始化爲 0,對於條件節點,該字段被初始化爲 CONDITION。 它使用 CAS 修改(或在可能的狀況下,無條件 volatile 寫入)。

await方法的實現

20ccbedb03a618267d27b81e563c6878.png

其實也沒作什麼,就建立了等待隊列和判斷在等待中被中斷。程序員

signal方法的實現

ad3a0c63bb4eca61fafd4fbabd42e297.png

小結

關於AQs與synchronized關鍵字之間的關係:

  1. synchronized關鍵字在底層的c++實現中,存在兩個重要的數據結構 (集合)︰waitSet,EntryList。
  2. waitSet中存放的是調用了object的wait方法的線程對象(被封裝成了C++的Node對象)。
  3. EntryList中存放的是陷入到阻塞狀態、須要獲取monitor的那些線程對象。
  4. 當一個線程被notify後,它就會從waitset中移動到EntryList中。
  5. 進入到EntryList後,該線程依然須要與其餘線程爭搶monitor對象。
  6. 若是爭搶到,就表示該線程獲取到了對象的鎖,它就能夠以排他方式執行對應的同步代碼。

  1. AQS中存在兩種隊列,分別是Condition對象上的條件隊列,以及AQS自己的阻塞隊列。
  2. 這兩個隊列中的每個對象都是Node實例(裏面封裝了線程對象),還有節點的狀態。
  3. 當位於Condition條件隊列中的線程被其餘線程signal後,該線程就會從條件隊列中移動到AQS的阻塞隊列中。
  4. 位於AQS阻塞隊列中的Node對象本質上都是由一個雙向鏈表來構成的。
  5. 在獲取AQS鎖時,這些進入到阻塞隊列中的線程會按照在隊列中的排序前後嘗試獲取。
  6. 當AQS阻塞隊列中的線程獲取到鎖後,就表示該線程已經能夠正常執行了。
  7. 陷入到阻塞狀態的線程,依然須要進入到操做系統的內核態,進入阻塞(park方法實現)。

waitSet對應AQS等待隊列,且後者能夠多個,且互不干擾。
EntryList對應AQS的阻塞隊列。面試

 

最後

最近我整理了整套《JAVA核心知識點總結》,說實話 ,做爲一名Java程序員,不論你需不須要面試都應該好好看下這份資料。拿到手老是不虧的~個人很多粉絲也所以拿到騰訊字節快手等公司的Offer數據結構

Java進階之路羣,找管理員獲取哦-!ide

05e3e61f93e261ef4268bde0f3e883ca.png

ca1866a6826f3e4004a34cb6bda189e2.png

相關文章
相關標籤/搜索