[toc]
JDK1.7分段代碼鎖的實現(xiàn)
和HashMap一樣在1.7中ConcurrentHashMap的底層數(shù)據(jù)結(jié)構(gòu)是數(shù)組加鏈表车份,和HashMap不同的是ConcurrentHashMap存放的數(shù)據(jù)是一段一段的,即由多個Segment組成华糖,每個Segment都有類似的數(shù)組加鏈表的結(jié)構(gòu)痰洒。
關(guān)于Segment:
ConturrentHashMap有三個參數(shù):
- initialCapacity:容量瓢棒,初始容量默認16
- loadFactor:負載因子,默認是0.75
- concurrentLevel:并發(fā)級別默認是16
其中concurrentLevel控制了Segment的個數(shù)丘喻,在一個ConcurrentHashMap創(chuàng)建后Sement的個數(shù)是不能變的脯宿,擴容過程改變的是每個Segement的大小。
Segement繼承了重入鎖ReentrantLock泉粉,有鎖的功能嗅绰,每個鎖控制的是一段,當每個Segement越來越大時搀继,鎖的力度就變的很大窘面。
- 優(yōu)點:分段代碼鎖的優(yōu)勢保證了操作不同的map的時候可以并發(fā)執(zhí)行,操作同段map的時候叽躯,進行鎖競爭和等待财边,這相對于直接對這個map同步synchronized是有優(yōu)勢的。
- 缺點:在于分成多段會比較浪費內(nèi)存空間(不連續(xù)点骑,碎片化)酣难,操作map時競爭同一個分段鎖的概率非常小,分段鎖反而造成更新等操作長時間等待黑滴,當某個段很大時憨募,分段性能會下降。
JDK1.8的ConcurrentHashMap實現(xiàn)
和hashMap一樣采用了數(shù)組鏈表紅黑樹的形式數(shù)組進行擴容袁辈,鏈表可以轉(zhuǎn)化為紅黑樹菜谣。
什么時候擴容?
- 當前容量超過閾值
- 當鏈表元素個數(shù)超過默認設(shè)定(8個),當數(shù)組大小還未超過64的時候尾膊,此時進行數(shù)組擴容媳危,如果超過64則將鏈表轉(zhuǎn)化為紅黑樹。
什么時候鏈表轉(zhuǎn)為紅黑樹冈敛?
當數(shù)組大小超過64并且鏈表中元素個數(shù)超過默認8的時候鏈表轉(zhuǎn)為紅黑樹待笑,但鏈表長度小于等于6時候會將紅黑樹轉(zhuǎn)為鏈表(紅黑樹保留鏈表特性)。
1.8的線程安全的實現(xiàn)
1.8的代碼把數(shù)組中每個元素看成一個桶抓谴,可以看到大部分的CAS操作暮蹂,加鎖部分是對桶的頭結(jié)點進行加鎖粒度很小。
為什么棄用Segement而用Synchroniized
- 減少內(nèi)存開銷癌压,如果使用ReentrantLock則需要節(jié)點繼承AQS來獲取同步支持仰泻,增加內(nèi)存開銷,而1.8只有頭部節(jié)點需要進行同步
- 內(nèi)部優(yōu)化:synchronized是jvm直接支持的措拇,jvm能夠運行時做出相應的優(yōu)化措施我纪,鎖粗化,鎖消除丐吓,鎖自旋浅悉,這使得synchronized能夠隨著JDK版本升級而不用改動代碼前提下獲得性能提升。