Java提供了不同層面的線程安全支持。在傳統集合框架內部,除了Hashtable等同步容器,還提供了所謂的同步包裝器(Synchronized Wrapper),我們可以調用Collections工具類提供的包裝方法,來獲取壹個同步的包裝容器(如Collections.synchronizedMap),但是它們都是利用非常粗粒度的同步方式,在高並發情況下,性能比較低下。
另外,更加普遍的選擇是利用並發包提供的線程安全容器類,它提供了:
各種並發容器,比如ConcurrentHashMap、 CopyOnWriteArrayList。
各種線程安全隊列(Queue/Deque),如ArrayBlockingQueue、 SynchronousQueue。
各種有序容器的線程安全版本等。
1.為什麽需要ConcurrentHashMap?
Hashtable本身比較低效,因為它的實現基本就是將put、 get、 size等各種方法加上“synchronized”。簡單來說,這就導致了所有並發操作都要競爭同壹把鎖,壹個線程在進行同
步操作時,其他線程只能等待,大大降低了並發操作的效率。
個人理解
就是更細粒度的加鎖機制來實現更大程度的***享。
2.ConcurrentHashMap分析
ConcurrentHashMap的設計實現其實壹直在演化。
1.7
put加鎖
通過分段加鎖segment,壹個hashmap裏有若幹個segment,每個segment裏有若幹個桶,桶裏存放K-V形式的鏈表, put數據時通過key哈希得到該元素要添加到的segment,
然後
對segment進行加鎖,然後在哈希,計算得到給元素要添加到的桶,然後遍歷桶中的鏈表,替換或新增節點到桶中
size
分段計算兩次,兩次結果相同則返回,否則對所以段加鎖重新計算
1.8
put CAS 加鎖
1.8中不依賴與segment加鎖,segment數量與桶數量壹致;
首先判斷容器是否為空,為空則進行初始化利用volatile的sizeCtl作為互斥手段,如果發現競爭性的初始化,就暫停在那裏,等待條件恢復,否則利用CAS設置排他標誌
(U.compareAndSwapInt(this, SIZECTL, sc, -1)) ;否則重試
對key hash計算得到該key存放的桶位置,判斷該桶是否為空,為空則利用CAS設置新節點
否則使用synchronize加鎖,遍歷桶中數據,替換或新增加點到桶中
最後判斷是否需要轉為紅黑樹,轉換之前判斷是否需要擴容
size
利用LongAdd累加計算
ConcurrentHashMap使用要點
ConcurrentHashMap使用要點
ConcurrentHashMap允許壹邊更新、壹邊遍歷,也就是說在Iterator對象遍歷的時候,ConcurrentHashMap也可以進行remove,put操作,且遍歷的數據會隨著remove,put操作產出變化