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