JUC 併發類及併發相關類概覽,持續補充...數組
- AQS
內部有兩個隊列,一個等待隊列(先後節點),一個條件隊列(後繼節點),實際上是經過鏈表方式實現;
等待隊列是雙向鏈表;條件隊列是單向鏈表;條件隊列若是被喚醒,將後接到等待隊列上;
經過內部持有的 state,加以模板模式,提供了兩種資源爭搶模式:排他、共享;而在爭搶時,又輔以公平、非公平競爭的考慮;
排他與共享分別經過子類實現的:boolean tryAcquire
和 (tryAcquireShared(x) >= 0
來判斷獲取資源是否成功,同時排他限制:state 只能被一個線程修改(通常修改成 1,CAS(0,1)
,可重入);而共享則支持多線程同時更改 state 值(被多個線程獲取);
獲取時要考慮是否關注中斷喚醒行爲,對應方法名後綴:-Interruptible
,關注則拋異常,不關注則清除線程的中斷標記位記錄,繼續掛起直到獲取成功,再中斷線程;
總的下來,分支考慮就是:資源類型(是否排他)-> 是否公平 -> 是否關注中斷(每個都是兩個方向)
;
釋放資源時,排他類型須要判斷是否當前線程,而共享類型不須要判斷線程,只關注資源池;排他類型會喚醒等待隊列的下一個節點,而共享則會經過級聯方式傳遞信號,從 head 開始喚醒,直到資源被新喚醒的線程搶光才中止;
線程的掛起是經過 LockSupport.park(thread)
,支持限時掛起;
state 釋放致使等待隊列判斷,進而喚醒。限時掛起一定支持中斷響應;
條件隊列由於初始時是經過 new ConditionObject()
,能夠屢次建立,因此支持多個條件隊列,使用同一個對象便是同一個條件隊列;被喚醒時會執行 state 的資源獲取,獲取不到則進入等待隊列;一樣支持是否中斷關注;而條件隊列的中斷有兩種狀況,一種在條件隊列上被中斷喚醒,一種是在進入等待隊列後被中斷喚醒;
AQS 主要是資源 state 的 CAS 併發及線程中斷掛起,排隊的控制
- AQLS
state 的類型爲 long,擴展了併發數(將來擴展)。其餘與 AQS 相同。
long 須要關注緩存行問題。
- ReentrantLock
基於 AQS 實現的鎖,設置 state = 1;支持公平與非公平(判斷隊列是否有等待線程);支持重入,state 遞增;競爭時只有 state = 0
才獲取鎖成功,不然進入等待隊列;
支持 condition 的 await 和 signal。
- CopyOnWriteArrayList
與 AQS 無關,使用的 synchronized 保證同步安全;當發生添加行爲時,會將原數組拷貝一份進行添加,而後更新引用;當發生指定索引添加時,會定位索引,分兩步進行拷貝(索引前、索引後);適合讀多寫少場景;addIfAbsent 會先判斷(無同步鎖),而後拷貝(有同步鎖),判斷時會先拷貝一份快照,在更新索引時判斷快照是否最新,是則直接使用,不然從新拷貝;
- CopyOnWriteArraySet
底層使用了 CopyOnWriteArrayList。惟一性的添加則是使用了 addIfAbsent。其餘大部分方法直接調用了 CopyOnWriteArrayList 的相似方法;
- CountDownLatch
使用了 AQS,初始化大小即爲 state,每次 countDown 都是對 state - 1
,而 await 則是在判斷 getState() == 0 ? 1 : -1
不經過掛起在隊列中,因此有多少 state 則其會被喚醒 state 次,而後從新掛起,直到 state = 0
;
- ArrayBlockingQueue
未直接使用或繼承 AQS,而是經過使用 ReentrantLock 和 Condition 來完成多線程同步與掛起;
內部使用數組存儲元素,但不會擴展,實現了環形隊列存儲,隊列元素增長 offer
及獲取 poll
都使用 lock 進行同步;
當添加成功時,進行 notEmpty.signal()
通知,喚醒獲取失敗的線程(notEmpty.await()
)處理;
當獲取成功時,進行 notFull.signal()
通知,喚添加失敗的線程(notFulll.await()
)處理;
offer(E)
添加返回 true/false
;add(E)
內部調用 offer
,返回 false 則拋異常;put
自實現,沒法添加則阻塞;
poll()
獲取返回 E/null
,take
自實現,若是獲取不到則阻塞掛起;
任何添加或獲取成功,相應的都會進行 notEmpty
與 notFull
通知,而失敗則進入相反的隊列掛起;ArrayBlockingQueue 的併發遍歷實現是經過隊列延展實現的(就是一個隊列,不斷的翻滾);相似於環形隊列