Java集合-ConcurrentHashMap

前面通過分析HashMap的數(shù)據(jù)結(jié)構(gòu)發(fā)現(xiàn)漾稀,HashMap通過Hash表和鏈表的結(jié)構(gòu)構(gòu)成的一個數(shù)據(jù)結(jié)構(gòu)设江,當(dāng)有兩個hash值相同的數(shù)據(jù)需要并發(fā)插入(刪除)時哎垦,可能會造成數(shù)據(jù)丟失的情況乙嘀。那么HashTable的存在就是為了保證Map的線程安全的,但是熙揍,當(dāng)Hashtable操作數(shù)據(jù)時,是將整個table都加入Sync氏涩,造成全部數(shù)據(jù)同時lock届囚,效率比較低。


HashMap和HashTable結(jié)構(gòu)

!

那么ConcurrentHashMap在并發(fā)方面對數(shù)據(jù)結(jié)構(gòu)做了微調(diào)是尖,同時結(jié)合了并發(fā)操作進行了一些算法改進意系。首先看一下ConcurrentHashMap的數(shù)據(jù)結(jié)構(gòu):

ConcurrentHashMap結(jié)構(gòu)【摘自網(wǎng)絡(luò)】

那么可以看出,他在HashTable的table結(jié)構(gòu)上再加了一層segment饺汹,ConcurrentHashMap就是通過加入多一層的segment蛔添,通過鎖去獨立控制每一個segment,來優(yōu)化數(shù)據(jù)不用同時lock首繁。
看一下segment的定義:

final Segment<K,V>[] segments;
static final class Segment<K,V> extends ReentrantLock implements Serializable{
    
    ... ...
}

其中Segment<K,V>繼承了ReentrantLock(重入鎖作郭,后續(xù)開文詳細分析),那么每一個segment都有一個單獨的lock進行管理和分配弦疮。
Segment<K,V>實現(xiàn)的是一個類似HashMap的結(jié)構(gòu)夹攒,如上圖可以看出,其實ConcurrentHashMap就是定義了一層Segment來包含每個單獨的HashMap胁塞,也可以膚淺的認為咏尝,ConcurrentHashMap就是由多個HashTable構(gòu)成的(每個HashTable獨立控制自己的鎖)压语。

那么,數(shù)據(jù)通過怎么樣落入到不同的segment中呢编检?這里同樣是使用hash算法進行segment的選擇:

private int hash(Object k) {
    int h = hashSeed;

    if ((0 != h) && (k instanceof String)) {
        return sun.misc.Hashing.stringHash32((String) k);
    }

    h ^= k.hashCode();

    // Spread bits to regularize both segment and index locations,
    // using variant of single-word Wang/Jenkins hash.
    h += (h <<  15) ^ 0xffffcd7d;
    h ^= (h >>> 10);
    h += (h <<   3);
    h ^= (h >>>  6);
    h += (h <<   2) + (h << 14);
    return h ^ (h >>> 16);
}

一眼看上去胎食,好長,不就一個hash算法么允懂,為什么會這么復(fù)雜厕怜,注釋中還涉及到了 Wang/Jenkins的hash算法。如果不詳細探討這個hash算法的數(shù)據(jù)理論的話蕾总,可以簡單的這么理解:如果使用一般的hash算法粥航,有可能造成很多的值同時落到一個segment中,那么最終這些數(shù)據(jù)都公用這個segment中的lock生百,那么多一層segments的結(jié)構(gòu)就起不到獨立鎖的目的递雀;所以這個算法就是用來調(diào)和讓每個segment都能落入接近數(shù)量的數(shù)據(jù),讓segments層的lock真正獨立起來蚀浆。

我們來看一下ConcurrentHashMap是怎么實現(xiàn)put方法的:

public V put(K key, V value) {
    Segment<K,V> s;
    if (value == null)
        throw new NullPointerException();
    int hash = hash(key);
    int j = (hash >>> segmentShift) & segmentMask;
    if ((s = (Segment<K,V>)UNSAFE.getObject          // nonvolatile; recheck
         (segments, (j << SSHIFT) + SBASE)) == null) //  in ensureSegment
        s = ensureSegment(j);
    return s.put(key, hash, value, false);
}

可以看到缀程,他是通過hash算法先找到對應(yīng)的segment,然后交由這個segement去處理put方法市俊,看一下segment的put方法:

final V put(K key, int hash, V value, boolean onlyIfAbsent) {
    HashEntry<K,V> node = tryLock() ? null :
                scanAndLockForPut(key, hash, value);
    ... ...
}

這里先通過ReentrantLock獲取鎖杨凑。然后,通過hash值計算秕衙,得到table的bucket中的鏈表 蠢甲,在獲取鏈表的第一個元素:

HashEntry<K,V>[] tab = table;
int index = (tab.length - 1) & hash;
HashEntry<K,V> first = entryAt(tab, index);

然后,看一下如果這個bucket為空的即鏈表為空的情況:

setEntryAt(tab, index, node);

將此對象設(shè)為header据忘,并將header放入table中鹦牛,如果bucket不為空時:

e = e.next;

類似hashmap的結(jié)構(gòu),把table的header換成當(dāng)前勇吊,并將當(dāng)前的next指針指向原來的header曼追。

putentry

ConcurrentHashMap暫時分析到這里,后續(xù)分析并發(fā)編程再重新回來分析這里的并發(fā)編程的技術(shù)汉规。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末礼殊,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子针史,更是在濱河造成了極大的恐慌晶伦,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件啄枕,死亡現(xiàn)場離奇詭異婚陪,居然都是意外死亡,警方通過查閱死者的電腦和手機频祝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門泌参,熙熙樓的掌柜王于貴愁眉苦臉地迎上來脆淹,“玉大人,你說我怎么就攤上這事沽一「悄纾” “怎么了?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵铣缠,是天一觀的道長烘嘱。 經(jīng)常有香客問我,道長攘残,這世上最難降的妖魔是什么拙友? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮歼郭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘辐棒。我一直安慰自己病曾,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布漾根。 她就那樣靜靜地躺著泰涂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辐怕。 梳的紋絲不亂的頭發(fā)上逼蒙,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天,我揣著相機與錄音寄疏,去河邊找鬼是牢。 笑死,一個胖子當(dāng)著我的面吹牛陕截,可吹牛的內(nèi)容都是我干的驳棱。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼农曲,長吁一口氣:“原來是場噩夢啊……” “哼社搅!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起乳规,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤形葬,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后暮的,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體笙以,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年青扔,在試婚紗的時候發(fā)現(xiàn)自己被綠了源织。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片翩伪。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖谈息,靈堂內(nèi)的尸體忽然破棺而出缘屹,到底是詐尸還是另有隱情,我是刑警寧澤侠仇,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布轻姿,位于F島的核電站,受9級特大地震影響逻炊,放射性物質(zhì)發(fā)生泄漏互亮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一余素、第九天 我趴在偏房一處隱蔽的房頂上張望豹休。 院中可真熱鬧,春花似錦桨吊、人聲如沸威根。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽洛搀。三九已至,卻和暖如春佑淀,著一層夾襖步出監(jiān)牢的瞬間留美,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工伸刃, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留谎砾,地道東北人。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓奕枝,卻偏偏與公主長得像棺榔,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子隘道,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,843評論 2 354

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