ConcurrentHashMap的實(shí)現(xiàn)原理(JDK1.7和JDK1.8)

HashMap、CurrentHashMap 的實(shí)現(xiàn)原理基本都是BAT面試必考內(nèi)容谊路,阿里P8架構(gòu)師談:深入探討HashMap的底層結(jié)構(gòu)讹躯、原理、擴(kuò)容機(jī)制深入談過hashmap的實(shí)現(xiàn)原理以及在JDK 1.8的實(shí)現(xiàn)區(qū)別缠劝,今天主要談CurrentHashMap的實(shí)現(xiàn)原理潮梯,以及在JDK1.7和1.8的區(qū)別。

1.哈希表
2.ConcurrentHashMap與HashMap剩彬、HashTable的區(qū)別
3.CurrentHashMap在JDK1.7和JDK1.8版本的區(qū)別

image.png

哈希表

1.介紹

哈希表就是一種以 鍵-值(key-indexed) 存儲(chǔ)數(shù)據(jù)的結(jié)構(gòu)酷麦,我們只要輸入待查找的值即key,即可查找到其對(duì)應(yīng)的值喉恋。
哈希的思路很簡(jiǎn)單,如果所有的鍵都是整數(shù),那么就可以使用一個(gè)簡(jiǎn)單的無序數(shù)組來實(shí)現(xiàn):將鍵作為索引轻黑,值即為其對(duì)應(yīng)的值糊肤,這樣就可以快速訪問任意鍵的值。這是對(duì)于簡(jiǎn)單的鍵的情況氓鄙,我們將其擴(kuò)展到可以處理更加復(fù)雜的類型的鍵馆揉。

2.鏈?zhǔn)焦1?/h5>

鏈?zhǔn)焦1韽母旧险f是由一組鏈表構(gòu)成。每個(gè)鏈表都可以看做是一個(gè)“桶”抖拦,我們將所有的元素通過散列的方式放到具體的不同的桶中升酣。插入元素時(shí),首先將其鍵傳入一個(gè)哈希函數(shù)(該過程稱為哈希鍵)态罪,函數(shù)通過散列的方式告知元素屬于哪個(gè)“桶”噩茄,然后在相應(yīng)的鏈表頭插入元素。查找或刪除元素時(shí)复颈,用同們的方式先找到元素的“桶”绩聘,然后遍歷相應(yīng)的鏈表,直到發(fā)現(xiàn)我們想要的元素耗啦。因?yàn)槊總€(gè)“桶”都是一個(gè)鏈表凿菩,所以鏈?zhǔn)焦1聿⒉幌拗瓢氐膫€(gè)數(shù)。然而帜讲,如果表變得太大衅谷,它的性能將會(huì)降低。


image.png
3.應(yīng)用場(chǎng)景

我們熟知的緩存技術(shù)(比如redis似将、memcached)的核心其實(shí)就是在內(nèi)存中維護(hù)一張巨大的哈希表获黔,還有大家熟知的HashMap、CurrentHashMap等的應(yīng)用玩郊。

ConcurrentHashMap與HashMap等的區(qū)別

1.HashMap

我們知道HashMap是線程不安全的肢执,在多線程環(huán)境下,使用Hashmap進(jìn)行put操作會(huì)引起死循環(huán)译红,導(dǎo)致CPU利用率接近100%预茄,所以在并發(fā)情況下不能使用HashMap。

2.HashTable

HashTable和HashMap的實(shí)現(xiàn)原理幾乎一樣侦厚,差別無非是

  • HashTable不允許key和value為null
  • HashTable是線程安全的

但是HashTable線程安全的策略實(shí)現(xiàn)代價(jià)卻太大了耻陕,簡(jiǎn)單粗暴,get/put所有相關(guān)操作都是synchronized的刨沦,這相當(dāng)于給整個(gè)哈希表加了一把大鎖诗宣。

多線程訪問時(shí)候,只要有一個(gè)線程訪問或操作該對(duì)象想诅,那其他線程只能阻塞召庞,相當(dāng)于將所有的操作串行化岛心,在競(jìng)爭(zhēng)激烈的并發(fā)場(chǎng)景中性能就會(huì)非常差。

3.ConcurrentHashMap

主要就是為了應(yīng)對(duì)hashmap在并發(fā)環(huán)境下不安全而誕生的篮灼,ConcurrentHashMap的設(shè)計(jì)與實(shí)現(xiàn)非常精巧忘古,大量的利用了volatile,final诅诱,CAS等lock-free技術(shù)來減少鎖競(jìng)爭(zhēng)對(duì)于性能的影響髓堪。
我們都知道Map一般都是數(shù)組+鏈表結(jié)構(gòu)(JDK1.8該為數(shù)組+紅黑樹)。


image.png

ConcurrentHashMap避免了對(duì)全局加鎖改成了局部加鎖操作娘荡,這樣就極大地提高了并發(fā)環(huán)境下的操作速度干旁,由于ConcurrentHashMap在JDK1.7和1.8中的實(shí)現(xiàn)非常不同,接下來我們談?wù)凧DK在1.7和1.8中的區(qū)別炮沐。

JDK1.7版本的CurrentHashMap的實(shí)現(xiàn)原理

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

1.Segment(分段鎖)

ConcurrentHashMap中的分段鎖稱為Segment,它即類似于HashMap的結(jié)構(gòu)央拖,即內(nèi)部擁有一個(gè)Entry數(shù)組祭阀,數(shù)組中的每個(gè)元素又是一個(gè)鏈表,同時(shí)又是一個(gè)ReentrantLock(Segment繼承了ReentrantLock)。

2.內(nèi)部結(jié)構(gòu)

ConcurrentHashMap使用分段鎖技術(shù)鲜戒,將數(shù)據(jù)分成一段一段的存儲(chǔ)专控,然后給每一段數(shù)據(jù)配一把鎖,當(dāng)一個(gè)線程占用鎖訪問其中一個(gè)段數(shù)據(jù)的時(shí)候遏餐,其他段的數(shù)據(jù)也能被其他線程訪問伦腐,能夠?qū)崿F(xiàn)真正的并發(fā)訪問。如下圖是ConcurrentHashMap的內(nèi)部結(jié)構(gòu)圖:


image.png

從上面的結(jié)構(gòu)我們可以了解到失都,ConcurrentHashMap定位一個(gè)元素的過程需要進(jìn)行兩次Hash操作柏蘑。

第一次Hash定位到Segment,第二次Hash定位到元素所在的鏈表的頭部粹庞。

3.該結(jié)構(gòu)的優(yōu)劣勢(shì)

壞處
這一種結(jié)構(gòu)的帶來的副作用是Hash的過程要比普通的HashMap要長(zhǎng)

好處
寫操作的時(shí)候可以只對(duì)元素所在的Segment進(jìn)行加鎖即可咳焚,不會(huì)影響到其他的Segment,這樣庞溜,在最理想的情況下革半,ConcurrentHashMap可以最高同時(shí)支持Segment數(shù)量大小的寫操作(剛好這些寫操作都非常平均地分布在所有的Segment上)。

所以流码,通過這一種結(jié)構(gòu)又官,ConcurrentHashMap的并發(fā)能力可以大大的提高。

JDK1.8版本的CurrentHashMap的實(shí)現(xiàn)原理

JDK8中ConcurrentHashMap參考了JDK8 HashMap的實(shí)現(xiàn)漫试,采用了數(shù)組+鏈表+紅黑樹的實(shí)現(xiàn)方式來設(shè)計(jì)六敬,內(nèi)部大量采用CAS操作,這里我簡(jiǎn)要介紹下CAS驾荣。

CAS是compare and swap的縮寫外构,即我們所說的比較交換普泡。cas是一種基于鎖的操作,而且是樂觀鎖典勇。在java中鎖分為樂觀鎖和悲觀鎖劫哼。悲觀鎖是將資源鎖住叮趴,等一個(gè)之前獲得鎖的線程釋放鎖之后割笙,下一個(gè)線程才可以訪問。而樂觀鎖采取了一種寬泛的態(tài)度眯亦,通過某種方式不加鎖來處理資源伤溉,比如通過給記錄加version來獲取數(shù)據(jù),性能較悲觀鎖有很大的提高妻率。

CAS 操作包含三個(gè)操作數(shù) —— 內(nèi)存位置(V)乱顾、預(yù)期原值(A)和新值(B)。如果內(nèi)存地址里面的值和A的值是一樣的宫静,那么就將內(nèi)存里面的值更新成B走净。CAS是通過無限循環(huán)來獲取數(shù)據(jù)的,若果在第一輪循環(huán)中孤里,a線程獲取地址里面的值被b線程修改了伏伯,那么a線程需要自旋,到下次循環(huán)才有可能機(jī)會(huì)執(zhí)行捌袜。

JDK8中徹底放棄了Segment轉(zhuǎn)而采用的是Node说搅,其設(shè)計(jì)思想也不再是JDK1.7中的分段鎖思想。

Node:保存key虏等,value及key的hash值的數(shù)據(jù)結(jié)構(gòu)弄唧。其中value和next都用volatile修飾,保證并發(fā)的可見性霍衫。

class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    volatile V val;
    volatile Node<K,V> next;
    //... 省略部分代碼
}

Java8 ConcurrentHashMap結(jié)構(gòu)基本上和Java8的HashMap一樣候引,不過保證線程安全性。
在JDK8中ConcurrentHashMap的結(jié)構(gòu)敦跌,由于引入了紅黑樹澄干,使得ConcurrentHashMap的實(shí)現(xiàn)非常復(fù)雜,我們都知道峰髓,紅黑樹是一種性能非常好的二叉查找樹傻寂,其查找性能為O(logN),但是其實(shí)現(xiàn)過程也非常復(fù)雜携兵,而且可讀性也非常差疾掰,Doug
Lea的思維能力確實(shí)不是一般人能比的,早期完全采用鏈表結(jié)構(gòu)時(shí)Map的查找時(shí)間復(fù)雜度為O(N)徐紧,JDK8中ConcurrentHashMap在鏈表的長(zhǎng)度大于某個(gè)閾值的時(shí)候會(huì)將鏈表轉(zhuǎn)換成紅黑樹進(jìn)一步提高其查找性能静檬。


image.png

總結(jié)

其實(shí)可以看出JDK1.8版本的ConcurrentHashMap的數(shù)據(jù)結(jié)構(gòu)已經(jīng)接近HashMap炭懊,相對(duì)而言,ConcurrentHashMap只是增加了同步的操作來控制并發(fā)拂檩,從JDK1.7版本的ReentrantLock+Segment+HashEntry侮腹,到JDK1.8版本中synchronized+CAS+HashEntry+紅黑樹。

1.數(shù)據(jù)結(jié)構(gòu):取消了Segment分段鎖的數(shù)據(jù)結(jié)構(gòu)稻励,取而代之的是數(shù)組+鏈表+紅黑樹的結(jié)構(gòu)父阻。
2.保證線程安全機(jī)制:JDK1.7采用segment的分段鎖機(jī)制實(shí)現(xiàn)線程安全,其中segment繼承自ReentrantLock望抽。JDK1.8采用CAS+Synchronized保證線程安全加矛。
3.鎖的粒度:原來是對(duì)需要進(jìn)行數(shù)據(jù)操作的Segment加鎖,現(xiàn)調(diào)整為對(duì)每個(gè)數(shù)組元素加鎖(Node)煤篙。
4.鏈表轉(zhuǎn)化為紅黑樹:定位結(jié)點(diǎn)的hash算法簡(jiǎn)化會(huì)帶來弊端,Hash沖突加劇,因此在鏈表節(jié)點(diǎn)數(shù)量大于8時(shí)斟览,會(huì)將鏈表轉(zhuǎn)化為紅黑樹進(jìn)行存儲(chǔ)。
5.查詢時(shí)間復(fù)雜度:從原來的遍歷鏈表O(n)辑奈,變成遍歷紅黑樹O(logN)苛茂。

轉(zhuǎn)自:http://youzhixueyuan.com/concurrenthashmap.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市鸠窗,隨后出現(xiàn)的幾起案子妓羊,更是在濱河造成了極大的恐慌,老刑警劉巖塌鸯,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件侍瑟,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡丙猬,警方通過查閱死者的電腦和手機(jī)涨颜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來茧球,“玉大人庭瑰,你說我怎么就攤上這事∏缆瘢” “怎么了弹灭?”我有些...
    開封第一講書人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)揪垄。 經(jīng)常有香客問我穷吮,道長(zhǎng),這世上最難降的妖魔是什么饥努? 我笑而不...
    開封第一講書人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任捡鱼,我火速辦了婚禮,結(jié)果婚禮上酷愧,老公的妹妹穿的比我還像新娘驾诈。我一直安慰自己缠诅,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開白布乍迄。 她就那樣靜靜地躺著管引,像睡著了一般。 火紅的嫁衣襯著肌膚如雪闯两。 梳的紋絲不亂的頭發(fā)上褥伴,一...
    開封第一講書人閱讀 50,096評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音生蚁,去河邊找鬼噩翠。 笑死,一個(gè)胖子當(dāng)著我的面吹牛邦投,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播擅笔,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼志衣,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了猛们?” 一聲冷哼從身側(cè)響起念脯,我...
    開封第一講書人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎弯淘,沒想到半個(gè)月后绿店,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡庐橙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年假勿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片态鳖。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡转培,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出浆竭,到底是詐尸還是另有隱情浸须,我是刑警寧澤,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布邦泄,位于F島的核電站删窒,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏顺囊。R本人自食惡果不足惜肌索,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望包蓝。 院中可真熱鬧驶社,春花似錦企量、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至份乒,卻和暖如春恕汇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背或辖。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工瘾英, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人颂暇。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓缺谴,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親耳鸯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子湿蛔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容

  • 誰扯著你 誰讓你明亮的眸子里 有了 那抹紅 到晚上 你被遺棄了吧 在白日 你是不是被那不舍 擁入懷中 要不 風(fēng)還說...
    本無痕閱讀 375評(píng)論 58 47
  • 捧圣閱讀 162評(píng)論 0 0