原文地址:https://cloud.tencent.com/developer/article/1509556java
推薦一篇 ConcurrentHashMap 和 HashMap 寫的比較的的文章數組
jdk1.7分段鎖的實現
和hashmap同樣,在jdk1.7中ConcurrentHashMap的底層數據結構是數組加鏈表。和hashmap不一樣的是ConcurrentHashMap中存放的數據是一段段的,即由多個Segment(段)組成的。每一個Segment中都有着相似於數組加鏈表的結構。數據結構
關於Segment
ConcurrentHashMap有3個參數:併發
- initialCapacity:初始總容量,默認16
- loadFactor:加載因子,默認0.75
- concurrencyLevel:併發級別,默認16
其中併發級別控制了Segment的個數,在一個ConcurrentHashMap建立後Segment的個數是不能變的,擴容過程過改變的是每一個Segment的大小。性能
關於分段鎖
段Segment繼承了重入鎖ReentrantLock,有了鎖的功能,每一個鎖控制的是一段,當每一個Segment愈來愈大時,鎖的粒度就變得有些大了。優化
- 分段鎖的優點在於保證在操做不一樣段 map 的時候能夠併發執行,操做同段 map 的時候,進行鎖的競爭和等待。這相對於直接對整個map同步synchronized是有優點的。
- 缺點在於分紅不少段時會比較浪費內存空間(不連續,碎片化); 操做map時競爭同一個分段鎖的機率很是小時,分段鎖反而會形成更新等操做的長時間等待; 當某個段很大時,分段鎖的性能會降低。
jdk1.8的map實現
和hashmap同樣,jdk 1.8中ConcurrentHashmap採用的底層數據結構爲數組+鏈表+紅黑樹的形式。數組能夠擴容,鏈表能夠轉化爲紅黑樹。spa
何時擴容?
- 當前容量超過閾值
- 當鏈表中元素個數超過默認設定(8個),當數組的大小還未超過64的時候,此時進行數組的擴容,若是超過則將鏈表轉化成紅黑樹
何時鏈表轉化爲紅黑樹?
當數組大小已經超過64而且鏈表中的元素個數超過默認設定(8個)時,將鏈表轉化爲紅黑樹.net
ConcurrentHashMap的put操做代碼以下:對象
把數組中的每一個元素當作一個桶。能夠看到大部分都是CAS操做,加鎖的部分是對桶的頭節點進行加鎖,鎖粒度很小。blog
爲何不用ReentrantLock而用synchronized ?
- 減小內存開銷:若是使用ReentrantLock則須要節點繼承AQS來得到同步支持,增長內存開銷,而1.8中只有頭節點須要進行同步。
- 內部優化:synchronized則是JVM直接支持的,JVM可以在運行時做出相應的優化措施:鎖粗化、鎖消除、鎖自旋等等。
總結:
經過源碼能夠看出 使用 CAS + synchronized 方式時 加鎖的對象是每一個鏈條的頭結點,也就是 鎖定 的是衝突的鏈表,因此再次提升了併發度,併發度等於鏈表的條數或者說 桶的數量。那爲何sement 不把段的大小設置爲一個桶呢,由於在粒度比較小的狀況下,若是使用ReentrantLock則須要節點繼承AQS來得到同步支持,增長內存開銷,而1.8中只有頭節點須要進行同步,粒度表較小,相對來講內存開銷就比較大。因此不把segment的大小設置爲一個桶。
參考
- https://my.oschina.net/pingpangkuangmo/blog/817973
- https://www.wanaright.com/2018/09/30/java10-concurrenthashmap-no-segment-lock/
- https://blog.csdn.net/mian_csdn/article/details/70185104
- https://cloud.tencent.com/developer/article/1509556
原文地址:https://cloud.tencent.com/developer/article/1509556
若有侵權,留言刪除