簡單總結(jié)ConcurrentHashMap

在并發(fā)使用到HashMap的時候,往往不建議直接用HashMap,因為HashMap在并發(fā)寫數(shù)據(jù)的時候容易因為rehash的過程產(chǎn)生環(huán)形鏈表的情況。所以在并發(fā)使用Map結(jié)構(gòu)時,一般建議使用ConcurrentHashMap。

1 JDK1.7 的ConcurrentHashMap

在JDK1.7中ConcurrentHashMap采用了數(shù)組+Segment+分段鎖的方式實現(xiàn)。

  • Segment(分段鎖):ConcurrentHashMap中的分段鎖稱為Segment,它即類似于HashMap的結(jié)構(gòu),即內(nèi)部擁有一個Entry數(shù)組,數(shù)組中的每個元素又是一個鏈表,同時又是一個ReentrantLock(Segment繼承了ReentrantLock)。
  • 內(nèi)部結(jié)構(gòu):ConcurrentHashMap使用分段鎖技術(shù),將數(shù)據(jù)分成一段一段的存儲,然后給每一段數(shù)據(jù)配一把鎖,當(dāng)一個線程占用鎖訪問其中一個段數(shù)據(jù)的時候,其他段的數(shù)據(jù)也能被其他線程訪問,能夠?qū)崿F(xiàn)真正的并發(fā)訪問。如下圖是ConcurrentHashMap的內(nèi)部結(jié)構(gòu)圖:
    image.png

從上面的結(jié)構(gòu)我們可以了解到,ConcurrentHashMap定位一個元素的過程需要進行兩次Hash操作。第一次Hash定位到Segment,第二次Hash定位到元素所在的鏈表的頭部。

2 JDK1.8之后的ConcurrentHashMap

JDK8中ConcurrentHashMap參考了JDK8 HashMap的實現(xiàn),采用了數(shù)組+鏈表+紅黑樹的實現(xiàn)方式來設(shè)計,內(nèi)部大量采用CAS操作。并發(fā)控制使?synchronized 和 CAS 來操作。(JDK1.6 以后 對 synchronized 鎖做了很多優(yōu)化) 整個看起來就像是優(yōu)化過且線程安全的 HashMap,雖然在 JDK1.8 中還能看到 Segment 的數(shù)據(jù)結(jié)構(gòu),但
是已經(jīng)簡化了屬性,只是為了兼容舊版本;

JDK1.8的Nod節(jié)點中value和next都用volatile修飾,保證并發(fā)的可見性。

可以理解為,synchronized 只鎖定當(dāng)前鏈表或紅??叉樹的?節(jié)點,這樣只要 hash 不沖突,就不會產(chǎn)?并發(fā),效率?提升 N 倍。


image.png

3 ConcurrentHashMap和HashTable的區(qū)別

Hashtable 和 JDK1.8 之前的 HashMap 的底層數(shù)據(jù)結(jié)構(gòu)類似都是采? 數(shù)組+鏈表 的形式,數(shù)組是 HashMap 的主體,鏈表則是主要為了解決哈希沖突?存在的;

Hashtable(同?把鎖) :使? synchronized 來保證線程安全,效率?常低下。當(dāng)?個線程訪問同步?法時,其他線程也訪問同步?法,可能會進?阻塞或輪詢狀態(tài),如使? put 添加元素,另?個線程不能使? put 添加元素,也不能使?get,競爭會越來越激烈效率越低;

總結(jié)一下:

  • ConcurrentHashMap不論1.7還是1.8,他的執(zhí)行效率都比HashTable要高的多,主要原因還是因為Hash Table使用了一種全表加鎖的方式。


    image.png

參考:

JavaGuide

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容