1. ConcurrentHashMap
- HashMap擴容產生死鎖
- 產生死鎖的原因是在多線程下,HashMap會產生死循環問題。
- HashTable效率太低
- 因爲是用synchronized來約束的,所以效率低
- ConcurrentHashMap的鎖分段技術能有效的提高效率
將數據分段化,沒一段都用不同的鎖來管理。就是所得粒度變大了,這樣就增大了效率,不是一杆子全打死。
1.1 ConcurrentHashMap數據結構
- segments數組
- ssize
- concurrencyLevel
- segmentMask= ssize-1
- segmentShift
- HashEntry
1.2 ConcurrentHashMap的操作
-
get操作
-
put操作
- 是否需要擴容
- 如何擴容
-
size操作
1.3 對ConcurrentHashMap的自我認識
-
首先與之比較就是HashMap和HashTable了,爲什麼不用這兩種呢?
因爲HashMap不是不是線程安全的,因爲其在多線程擴容情況下回產生死循環的情況,容易產生死鎖。
HashTable是線程安全的,但是其用的是重量級鎖synchronized,這個鎖的粒度很大,導致性能影響很嚴重。
ConcurrentHashMap如其名稱,是線程安全的,其相比較於HashTable的優勢就是鎖的粒度小,採用分段加鎖的策略,性能相比較HashTable有了一定的提高。 -
ConcurrentHashMap的底層數據結構是Segment 和HashEntry.
Segment數據結構是一個數數組,但是其下標是2的n次方,我也自是瞭解,但是對其爲何這樣設計還不是很清楚 -
ConcurrentHashMap的操作有三個
get操作高效的原因是,其要獲取的數據是用volatile修飾的,而不需要加鎖,不加鎖性能肯定要快很多。但是爲什麼線程安全,get操作沒有寫操作,自然不存在競爭問題。put操作需要判斷Segment中的HashEntry是否需要擴容,然後定位到HashEnrty位置,將數據插入到對應的位置上。
size操作有些意思,因爲大小會動態變化,最保險的做法就是加鎖統計size,但是這樣會影響性能。其採用的策略就是,執行兩次不加鎖的統計, 通過在使用一個modCount變量來記錄對集合大小影響的操作數,當兩次的modCount是相同的,就代表大小沒有變化,可以使用不加鎖統計的大小。 當不一樣時,就執行加統計大小。
2. ConcurrentLinkedQueue 用非阻塞方式實現安全隊列
組成很簡單,就是隊列和隊列節點要素。
頭節點,尾節點 item next。
關於維護尾節點 offer
這裏還比較新奇,並不是每次插入節點都要更新尾節點tail指向的值。 其用一個變量HOPS來就按當前尾節點指向節點與隊列最後最後一個節點之間的距離,當距離到達一定的界限就進行一次尾節點tail的更新。
出隊 poll()
其實也就是維護頭結點
這裏有一個問題,怎麼實現安全性,因爲多線程下,暫時考慮的頭結點在其他線程中已經出隊了,怎麼解決這種數據不一致的問題,而且還不是運用加鎖來解決這個問題。
出隊也運用了HOPS策略來維護頭結點。
其實就是判斷頭結點是否爲空,如果不爲空就使用原子操作CAS出隊,如果爲空就在定位頭結點。其實也就是運用了CAS操作,在關鍵的出隊操作弄成原子操作,這樣就不會出現數據不一致問題。
3. 阻塞隊列
有插入、移除操作
阻塞隊列常用語生產者消費者模式。阻塞隊列就是兩者交換東西的地方,這兩個不見面,有點像電影裏的你把錢放在垃圾箱裏,我等會去取,兩個不見面,只通過消息來傳遞我放了錢,我去了錢。
LinkedBlockingQueue
ArrayBlockingQueue
DelayQueue
延時獲取隊列使用的範圍還是很廣的,任何需要延時獲取的都可以放進延時隊列中,在延時隊列中根據延時的時長,時間到了就能獲取。
該隊列宗有兩個方法
-
getDelay(TimeUnit unit)
-
compareTo(Delayed o)
另個方法的排序規則要相同。
LinkedTransferQueue
將消費者要消費的資源直接傳給消費者,其效率還是很快的,該隊列並不存儲任何資源。
- tranfer()
- tryTranfer()
嘗試該資源能不能直接傳給消費者,能就可以傳輸。
LinkedBlockingDeque
- 無界雙向鏈表阻塞隊列。
阻塞隊列的實現原理
4. Fork/Join框架
的一個用於並行執行任務的框架,是一個把大任務分割成若干 個小任務,最終彙總每個小任務結果後得到大任務結果的框架。
4.1 工作竊取算法
用雙端隊列存儲任務,一個別竊取的從偷拿,竊取的從尾部拿
4.2 Fork/Join框架的設計
- 分割任務
ForkJoinTask
RecursiveAction
RecursiveTask - 執行任務併合並結果
ForkJoinPool- ForkJoinTask
- ForkJoinWorkerThread
Fork/Join框架的設計感悟
這個框架的任務就是將一個大的任務分割成多個小的任務,這裏就要設計分頁大小了。ForkJoinTash實現的就是分割任務,將分割的任務放在ForkJoinTash數組中。
分割主要使用的方法就是fork方法。
被分割的任務在ForkJoinPool中進通過ForkJoinWorkerThread工作線程進行執行。其中用到的方法主要是Join()和doJoin()方法。
現在只是看書,沒有實際操作,等會面遇到了實際情況,運用該框架就熟悉了。