ConcurrentHashMap熟菲、Hashtable、HashMap

一更米、區(qū)別總結(jié)

1??HashMap非線程安全的欺栗,不適用于多線程資源共享場景,在單線程場景下性能最好征峦。在多線程環(huán)境中迟几,需要手動實現(xiàn)同步機制。HashMap 中栏笆,null 可以作為鍵类腮,這樣的鍵只有一個,但可以有一個或多個鍵所對應(yīng)的值為 null蛉加。當(dāng) get() 返回 null 時蚜枢,既可以表示 HashMap 中沒有該 key,也可以表示該 key 所對應(yīng)的 value 為 null针饥。因此厂抽,在 HashMap 中不能用 get() 來判斷是否存在某個 key,應(yīng)該用 containsKey() 來判斷丁眼。

2??Hashtable 的 API 都是 sychronized 的筷凤,因此是線程安全的,可以直接用在多線程環(huán)境中苞七。由于 Hashtable 中的所有數(shù)據(jù)都加了同步鎖藐守,因此性能最差。Hashtable 中蹂风,無論是 key 還是 value 都不能為 null卢厂。

3??HashMap 的迭代器(Iterator)是 fail-fast 迭代器,而 Hashtable 的 enumerator 迭代器不是 fail-fast 的硫眨。所以當(dāng)有其它線程改變了 HashMap 的結(jié)構(gòu)(增加或者移除元素)足淆,將會拋出 ConcurrentModificationException巢块,但迭代器本身的 remove() 移除元素則不會拋出 ConcurrentModificationException。但這并不是一定發(fā)生的行為巧号,要看 JVM族奢。

4??Hashtable 和 HashMap 都實現(xiàn)了 Map 接口,但是 Hashtable 的實現(xiàn)是基于 Dictionary 抽象類的丹鸿。Java5 提供了 ConcurrentHashMap越走,它是 Hashtable 的替代,比 Hashtable 的擴展性更好靠欢。ConcurrentHashMap 在 HashMap 的基礎(chǔ)上對其所存的數(shù)據(jù)進行了分段廊敌,每個分段都有一個鎖,當(dāng)讀的時候不受鎖的影響门怪,只有在寫的時候受鎖的限制骡澈,縮小了鎖的范圍,不同段之間不受鎖競爭影響掷空,既保證了線程安全肋殴,又提升了性能。ConcurrentHashMap 類是 Java 并發(fā)包 java.util.concurrent中提供的一個線程安全且高效的 HashMap 實現(xiàn)坦弟。在 JDK7 中采用分段鎖的方式护锤;JDK8 中直接采用了 CAS(無鎖算法)+ sychronized

5??Segment 類繼承于 ReentrantLock 類酿傍,從而使得 Segment 對象能充當(dāng)鎖的角色烙懦。每個 Segment 對象用來守護其(成員對象 table 中)包含的若干個桶。

table 是一個由 HashEntry 對象組成的數(shù)組赤炒。table 數(shù)組的每一個數(shù)組成員就是散列映射表的一個桶氯析。

count 變量是一個計數(shù)器,它表示每個 Segment 對象管理的 table 數(shù)組(若干個 HashEntry 組成的鏈表)包含的 HashEntry 對象的個數(shù)可霎。每一個 Segment 對象都有一個 count 對象來表示本 Segment 中包含的 HashEntry 對象的總數(shù)魄鸦。注意,之所以在每個 Segment 對象中包含一個計數(shù)器癣朗,而不是在 ConcurrentHashMap 中使用全局的計數(shù)器拾因,是為了避免出現(xiàn)“熱點域”而影響 ConcurrentHashMap 的并發(fā)性。

二旷余、ConcurrentHashMap(線程安全)

  • 底層采用分段的數(shù)組+鏈表實現(xiàn)
  • 通過把整個 Map 分為N個 Segment绢记,可以提供相同的線程安全,但是效率提升N倍正卧,默認提升16倍蠢熄。(讀操作不加鎖,由于 HashEntry 的 value 變量是 volatile 的炉旷,也能保證讀取到最新的值签孔。)
  • Hashtable 的 synchronized 是針對整張 Hash 表的叉讥,即每次鎖住整張表讓線程獨占,ConcurrentHashMap 允許多個修改操作并發(fā)進行饥追,其關(guān)鍵在于使用了鎖分離技術(shù)图仓。
  • 有些方法需要跨段,比如 size() 和 containsValue()但绕,它們可能需要鎖定整個表而不僅僅是某個段救崔,這需要按順序鎖定所有段,操作完畢后捏顺,又按順序釋放所有段的鎖六孵。
  • 擴容:段內(nèi)擴容(段內(nèi)元素超過該段對應(yīng) Entry 數(shù)組長度的 75% 觸發(fā)擴容,不會對整個 Map 進行擴容)幅骄,插入前檢測是否需要擴容劫窒,避免無效擴容。

從類圖可看出在存儲結(jié)構(gòu)中 ConcurrentHashMap 比 HashMap 多出了一個類 Segment昌执,而 Segment 是一個可重入鎖烛亦。ConcurrentHashMap 是使用了鎖分段技術(shù)來保證線程安全的诈泼。

鎖分段技術(shù):
首先將數(shù)據(jù)分成一段一段的存儲懂拾,然后給每一段數(shù)據(jù)配一把鎖,當(dāng)一個線程占用鎖訪問其中一個段數(shù)據(jù)的時候铐达,其他段的數(shù)據(jù)仍能被其他線程訪問岖赋。

ConcurrentHashMap 提供了與 Hashtable 和 SynchronizedMap 不同的鎖機制。Hashtable 中采用的鎖機制是一次鎖住整個 hash 表瓮孙,從而在同一時刻只能由一個線程對其進行操作唐断;而 ConcurrentHashMap 中則是一次鎖住一個桶。

ConcurrentHashMap 默認將 hash 表分為16個桶杭抠,諸如 get脸甘、put、remove 等常用操作只鎖住當(dāng)前需要用到的桶偏灿。這樣丹诀,原來只能一個線程進入,現(xiàn)在卻能同時有16個寫線程執(zhí)行翁垂,并發(fā)性能的提升是顯而易見的铆遭。

三、Hashtable(線程安全)

  • 底層采用數(shù)組+鏈表實現(xiàn)沿猜。key 和 value 都不能為 null枚荣。實現(xiàn)線程安全的方式是在修改數(shù)據(jù)時鎖住整個 Hashtable,效率低啼肩。
  • 初始 size 為11橄妆,擴容:newsize = oldsize*2+1
  • 計算 index 的方法:index = (hash & 0x7FFFFFFF) % tab.length

四衙伶、hash表的屬性:加載因子/負載極限

1??HashMap 和 Hashtable 都是用 hash 算法來決定其元素的存儲,因此HashMap 和 Hashtable 的 hash 表包含如下屬性:
①容量(capacity):hash 表中桶(buckets)的數(shù)量害碾;圖中的標(biāo)有0痕支、1、2蛮原、3 … 11所對應(yīng)的數(shù)組空間就是一個個桶卧须。
②初始化容量(initial capacity):創(chuàng)建 hash 表時桶的數(shù)量,HashMap 允許在構(gòu)造器中指定初始化容量儒陨;
③尺寸(size):當(dāng)前 hash 表中記錄的數(shù)量花嘶;
④加載因子(load factor):加載因子等于“size/capacity”。加載因子為0蹦漠,表示空的hash表椭员,0.5表示半滿的散列表,依此類推笛园。輕負載的散列表具有沖突少隘击、適宜插入與查詢的特點(但是使用Iterator迭代元素時比較慢)。
除此之外研铆,hash表里還有一個“負載極限”埋同,“負載極限”是一個0~1的數(shù)值,“負載極限”決定了hash表的最大填滿程度棵红。當(dāng)hash表中的加載因子達到指定的“負載極限”時凶赁,hash表會自動成倍地增加容量(桶的數(shù)量),并將原有的對象重新分配逆甜,放入新的桶內(nèi)虱肄,這稱為rehashing。
加載因子:為了降低哈希沖突的概率交煞,默認當(dāng)HashMap中的鍵值對達到數(shù)組大小的75%時咏窿,即會觸發(fā)擴容。因此素征,如果預(yù)估容量是75集嵌,即需要設(shè)定75/0.75=100的數(shù)組大小。
⑤哈希沖突:若干Key的哈希值按數(shù)組大小取模后稚茅,如果落在同一個數(shù)組下標(biāo)上纸淮,將組成一條Entry鏈,對Key的查找需要遍歷Entry鏈上的每個元素執(zhí)行equals()比較亚享。
⑥空間換時間:如果希望加快Key查找的時間咽块,還可以進一步降低加載因子,加大初始大小欺税,以降低哈希沖突的概率侈沪。

HashMap 和 Hashtable的構(gòu)造器允許指定一個負載極限揭璃,HashMap 和 Hashtable默認的“負載極限”為0.75,這表明當(dāng)該hash表的3/4已經(jīng)被填滿時亭罪,hash 表會發(fā)生 rehashing瘦馍。

2??“負載極限”值可以根據(jù)實際情況來調(diào)整,默認值(0.75)是時間和空間成本上的一種折中:

  • 較高的“負載極限”可以降低 hash 表所占用的內(nèi)存空間应役,但會增加查詢數(shù)據(jù)的時間開銷情组,而查詢是最頻繁的操作。HashMap 的 get() 與 put() 都要用到查詢
  • 較低的“負載極限”會提高查詢數(shù)據(jù)的性能箩祥,但會增加 hash 表所占用的內(nèi)存開銷院崇。

3??影響 HashMap 性能的兩個因素:哈希表中的初始化容量(桶的數(shù)量)和加載因子
當(dāng)哈希表中條目數(shù)超過了當(dāng)前容量與加載因子的乘積時,哈希表將會作出自我調(diào)整袍祖,將容量擴充為原來的兩倍底瓣,并且重新將原有的元素重新映射到表中,這一過程成為 rehash蕉陋。rehash 操作是會造成時間與空間的開銷的捐凭,因此初始化容量與加載因子會影響 HashMap 的性能。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末凳鬓,一起剝皮案震驚了整個濱河市茁肠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌村视,老刑警劉巖官套,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蚁孔,居然都是意外死亡,警方通過查閱死者的電腦和手機惋嚎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進店門杠氢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人另伍,你說我怎么就攤上這事鼻百。” “怎么了摆尝?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵温艇,是天一觀的道長。 經(jīng)常有香客問我堕汞,道長勺爱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任讯检,我火速辦了婚禮琐鲁,結(jié)果婚禮上卫旱,老公的妹妹穿的比我還像新娘。我一直安慰自己围段,他們只是感情好顾翼,可當(dāng)我...
    茶點故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著奈泪,像睡著了一般适贸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涝桅,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天取逾,我揣著相機與錄音,去河邊找鬼苹支。 笑死砾隅,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的债蜜。 我是一名探鬼主播晴埂,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼寻定!你這毒婦竟也來了儒洛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤狼速,失蹤者是張志新(化名)和其女友劉穎琅锻,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體向胡,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡恼蓬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了僵芹。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片处硬。...
    茶點故事閱讀 39,773評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖拇派,靈堂內(nèi)的尸體忽然破棺而出荷辕,到底是詐尸還是另有隱情,我是刑警寧澤件豌,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布疮方,位于F島的核電站,受9級特大地震影響茧彤,放射性物質(zhì)發(fā)生泄漏骡显。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蟆盐。 院中可真熱鬧承边,春花似錦、人聲如沸石挂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽痹愚。三九已至富岳,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間拯腮,已是汗流浹背窖式。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留动壤,地道東北人萝喘。 一個月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像琼懊,于是被迫代替她去往敵國和親阁簸。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,689評論 2 354