AQS大體結構圖
- 前面講解的可重入鎖和可重入讀寫鎖都是圍繞着阻塞隊列講解的。
- 沒有提到AQS中的另外一個重要內容:等待隊列,也稱之爲條件(condition)隊列。
- AQS有Node對象,其有兩個用途:造成等待隊列和阻塞隊列。
- 雖然是Node,挺像鏈表的,可是jdk的註釋中只用了queues這個單詞,因此都稱之爲隊列,畢竟它傾向於FIFO。
以ReentrantLock中的newCondition爲例
不厭其煩的閱讀jdk源碼c++
newCondition()方法
關於AQS中的Node的狀態
- 狀態字段,僅取值: SIGNAL:此節點的後繼節點被(或即將)阻塞(經過park),所以當前節點在釋放或取消時必須解除其後繼節點的park。 爲了不競爭,獲取方法必須首先代表它們須要一個信號(signal),而後重試原子獲取,而後在失敗時阻塞。 CANCELLED:因爲超時或中斷,該節點被取消。 節點永遠不會離開這個狀態。 特別是,取消節點的線程永遠不會再次阻塞。 CONDITION:該節點當前在條件隊列中。 它在傳輸以前不會用做同步隊列節點,此時狀態將設置爲 0。(此處使用此值與該字段的其餘用途無關,但簡化了機制。) PROPAGATE:A releaseShared 應該傳播到其餘節點。 這在 doReleaseShared 中設置(僅適用於頭節點)以確保傳播繼續,即便其餘操做已經介入。 0:以上都不是 爲了簡化使用,數值按數字排列。 非負值意味着節點不須要發出信號,通知後繼節點。 所以,大多數代碼不須要檢查特定值,只需檢查符號。 對於普通同步節點,該字段被初始化爲 0,對於條件節點,該字段被初始化爲 CONDITION。 它使用 CAS 修改(或在可能的狀況下,無條件 volatile 寫入)。
await方法的實現
其實也沒作什麼,就建立了等待隊列和判斷在等待中被中斷。程序員
signal方法的實現
小結
關於AQs與synchronized關鍵字之間的關係:
- synchronized關鍵字在底層的c++實現中,存在兩個重要的數據結構 (集合)︰waitSet,EntryList。
- waitSet中存放的是調用了object的wait方法的線程對象(被封裝成了C++的Node對象)。
- EntryList中存放的是陷入到阻塞狀態、須要獲取monitor的那些線程對象。
- 當一個線程被notify後,它就會從waitset中移動到EntryList中。
- 進入到EntryList後,該線程依然須要與其餘線程爭搶monitor對象。
- 若是爭搶到,就表示該線程獲取到了對象的鎖,它就能夠以排他方式執行對應的同步代碼。
- AQS中存在兩種隊列,分別是Condition對象上的條件隊列,以及AQS自己的阻塞隊列。
- 這兩個隊列中的每個對象都是Node實例(裏面封裝了線程對象),還有節點的狀態。
- 當位於Condition條件隊列中的線程被其餘線程signal後,該線程就會從條件隊列中移動到AQS的阻塞隊列中。
- 位於AQS阻塞隊列中的Node對象本質上都是由一個雙向鏈表來構成的。
- 在獲取AQS鎖時,這些進入到阻塞隊列中的線程會按照在隊列中的排序前後嘗試獲取。
- 當AQS阻塞隊列中的線程獲取到鎖後,就表示該線程已經能夠正常執行了。
- 陷入到阻塞狀態的線程,依然須要進入到操做系統的內核態,進入阻塞(park方法實現)。
waitSet對應AQS等待隊列,且後者能夠多個,且互不干擾。
EntryList對應AQS的阻塞隊列。面試
最後
最近我整理了整套《JAVA核心知識點總結》,說實話 ,做爲一名Java程序員,不論你需不須要面試都應該好好看下這份資料。拿到手老是不虧的~個人很多粉絲也所以拿到騰訊字節快手等公司的Offer數據結構
進【Java進階之路羣】,找管理員獲取哦-!ide
![05e3e61f93e261ef4268bde0f3e883ca.png](http://static.javashuo.com/static/loading.gif)
![ca1866a6826f3e4004a34cb6bda189e2.png](http://static.javashuo.com/static/loading.gif)