HashMap&CurrentHashMap

java7中的hashMap和currentHashMap

hashMap

數(shù)據(jù)結(jié)構(gòu)

數(shù)組加鏈表

尋址方式

將key的哈希值對(duì)數(shù)組長(zhǎng)度進(jìn)行取模,結(jié)果作為該Entry在數(shù)組中的index

擴(kuò)容

  • 為什么要擴(kuò)容
    • 會(huì)存在多個(gè)Entry對(duì)應(yīng)一個(gè)數(shù)組的情況(哈希沖突),當(dāng)都一個(gè)數(shù)組后面的鏈表特別長(zhǎng)的時(shí)候,進(jìn)行遍歷鏈表順序查找效率很低苦囱。所以為了提高性能榜聂,就會(huì)盡量縮小每個(gè)鏈表的長(zhǎng)度唉俗。
  • 擴(kuò)容時(shí)機(jī)
    • 當(dāng)map中包含的Entry的數(shù)量大于等于 threshold = loadFactor * capacity
    • 且 新建的Entry剛好落在一個(gè)非空的數(shù)組上
  • 擴(kuò)容機(jī)制
    • 創(chuàng)建一個(gè)新表
    • 重新生成hash值
    • 頭插法插入新表中

線(xiàn)程不安全闹司?

參考這篇文章

  • ==resize死循環(huán)==

上面知道了擴(kuò)容機(jī)制娱仔,擴(kuò)容時(shí)有個(gè)transfer方法

void transfer(Entry[] newTable, boolean rehash) {
  int newCapacity = newTable.length;
  for (Entry<K,V> e : table) {
    while(null != e) {
    // 1
      Entry<K,V> next = e.next;
      if (rehash) {
        e.hash = null == e.key ? 0 : hash(e.key);
      }
      int i = indexFor(e.hash, newCapacity);
     // 2
      e.next = newTable[i];
      newTable[i] = e;
      e = next;
    }
  }
}
  • 假如有兩個(gè)線(xiàn)程同時(shí)執(zhí)行transfer
  1. 線(xiàn)程A獲取到e=3,e.next=7,暫停了

  2. 線(xiàn)程B執(zhí)行完了游桩,即7->3->null牲迫,此時(shí)的狀態(tài)


    image
  3. 這時(shí)候A執(zhí)行,獲取到7的next是3,造成死循環(huán)

image
  1. 最后的結(jié)果


    image
  • ==fail-fast==
    在使用迭代器的過(guò)程中如果hashMap被修改借卧,則會(huì)拋出ConcurrentModificationException異常盹憎,也就是fast-fail策略。
HashIterator() {
// 在iterator的next方法訪(fǎng)問(wèn)下一個(gè)Entry事铐刘,會(huì)做這個(gè)檢查陪每,如果不相等,說(shuō)明被修改镰吵,拋出異常
  expectedModCount = modCount;
  
  if (size > 0) { // advance to first entry
  Entry[] t = table;
  while (index < t.length && (next = t[index++]) == null)
    ;
  }
}
  • fail-safe
    fail-safe任何對(duì)集合結(jié)構(gòu)的修改都會(huì)在一個(gè)復(fù)制的集合上進(jìn)行修改奶稠,因此不會(huì)拋出ConcurrentModificationException

fail-safe機(jī)制有兩個(gè)問(wèn)題

1需要復(fù)制集合,產(chǎn)生大量的無(wú)效對(duì)象捡遍,開(kāi)銷(xiāo)大

2 無(wú)法保證讀取的數(shù)據(jù)是目前原始數(shù)據(jù)結(jié)構(gòu)中的數(shù)據(jù)

currentHashMap

  • 采用分段鎖的結(jié)構(gòu)


    image
  • Segment默認(rèn)是16,理論上竹握,最多同時(shí)支持16個(gè)線(xiàn)程并發(fā)讀寫(xiě)画株,但是是操作不同的Segment
  • 初始化時(shí)可以指定Segment數(shù)量
  • Segment繼承自ReentrantLock,每一個(gè)Segment都會(huì)有一把鎖,保證線(xiàn)程安全

不同之處

ConcurrentHashMap與HashMap相比啦辐,有以下不同點(diǎn)

  • ConcurrentHashMap線(xiàn)程安全谓传,而HashMap非線(xiàn)程安全
  • HashMap允許Key和Value為null,而ConcurrentHashMap不允許
  • HashMap不允許通過(guò)Iterator遍歷的同時(shí)通過(guò)HashMap修改芹关,而ConcurrentHashMap允許該行為续挟,并且該更新對(duì)后續(xù)的遍歷可見(jiàn)

java8中的hashMap和currentHashMap

hashMap

與1.7不同的是,鏈表的元素超過(guò)8個(gè)以后侥衬,會(huì)講鏈表轉(zhuǎn)換成紅黑樹(shù)诗祸,時(shí)間復(fù)雜度由o(n)變?yōu)閛(logN)

currentHashMap

  • 結(jié)構(gòu)

    • 和hashMap基本一直,數(shù)組+鏈表+紅黑樹(shù),通過(guò)CAS+Synchronized保證線(xiàn)程安全轴总。
  • 初始化

  • 擴(kuò)容

  • 數(shù)據(jù)遷移
    下面分開(kāi)說(shuō)

  • sizeCtl

    • 是一個(gè)控制標(biāo)識(shí)符直颅,取值不同,含義不同
    • -1代表正在初始化
    • -N表示有N-1個(gè)線(xiàn)程正在進(jìn)行擴(kuò)容操作(允許多線(xiàn)程擴(kuò)容)
    • 正數(shù)或0代表還沒(méi)有被初始化

    private transient volatile int sizeC怀樟;
  • 初始化數(shù)組
private final Node<K,V>[] initTable() {
    Node<K,V>[] tab; int sc;
    while ((tab = table) == null || tab.length == 0) {
        // 上面說(shuō)過(guò)<0代表被其他線(xiàn)程正在初始化
        if ((sc = sizeCtl) < 0)
            Thread.yield(); // lost initialization race; just spin
        // CAS 一下功偿,將 sizeCtl 設(shè)置為 -1,代表?yè)尩搅随i
        else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
            ……
            
            break;
        }
    }
    return tab;
}
  • 擴(kuò)容
// 首先要說(shuō)明的是往堡,方法參數(shù) size 傳進(jìn)來(lái)的時(shí)候就已經(jīng)翻了倍了
private final void tryPresize(int size) {
 ***
    while ((sc = sizeCtl) >= 0) {
        Node<K,V>[] tab = table; int n;

        // 這個(gè) if 分支和之前說(shuō)的初始化數(shù)組的代碼基本上是一樣的械荷,在這里共耍,我們可以不用管這塊代碼
        if (tab == null || (n = tab.length) == 0) {
          ****
        }
        else if (c <= sc || n >= MAXIMUM_CAPACITY)
            break;
        else if (tab == table) {
            // 我沒(méi)看懂 rs 的真正含義是什么,不過(guò)也關(guān)系不大
            int rs = resizeStamp(n);

            if (sc < 0) {
                Node<K,V>[] nt;
                if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
                    sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
                    transferIndex <= 0)
                    break;
                // 2. 用 CAS 將 sizeCtl 加 1吨瞎,然后執(zhí)行 transfer 方法
                //    此時(shí) nextTab 不為 null
                if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
                    transfer(tab, nt);
            }
            // 1. 將 sizeCtl 設(shè)置為 (rs << RESIZE_STAMP_SHIFT) + 2)
            //     我是沒(méi)看懂這個(gè)值真正的意義是什么痹兜?不過(guò)可以計(jì)算出來(lái)的是,結(jié)果是一個(gè)比較大的負(fù)數(shù)
            //  調(diào)用 transfer 方法关拒,此時(shí) nextTab 參數(shù)為 null
            else if (U.compareAndSwapInt(this, SIZECTL, sc,
                                         (rs << RESIZE_STAMP_SHIFT) + 2))
                transfer(tab, null);
        }
    }
}
  • 數(shù)據(jù)遷移
    允許多線(xiàn)程
    原理:
    假設(shè)數(shù)組長(zhǎng)度為n佃蚜,讓每個(gè)線(xiàn)程負(fù)責(zé)一個(gè)小人物,做完一個(gè)再去做另一個(gè)着绊,使用了一個(gè)stride作為步長(zhǎng)(每次遷移的任務(wù)長(zhǎng)度),然后還需要一個(gè)全局的調(diào)度者安排哪個(gè)線(xiàn)程執(zhí)行哪一段归露,這就是transferIndex屬性的作用

  • 第一個(gè)線(xiàn)程會(huì)將ransferIndex執(zhí)行原數(shù)組最后的位置洲脂,然后從后往前stride個(gè)任務(wù)屬于它- 然后ransferIndex指向新位置,分給第二個(gè)線(xiàn)程

什么是紅黑樹(shù)

  1. 每個(gè)節(jié)點(diǎn)要么是紅要么是黑
  2. 根節(jié)點(diǎn)是黑
  3. 每個(gè)葉節(jié)點(diǎn)都是黑(葉節(jié)點(diǎn)指樹(shù)尾端NIL或null節(jié)點(diǎn))
  4. 如果一個(gè)節(jié)點(diǎn)是紅的剧包,子節(jié)點(diǎn)就是黑的
  5. 對(duì)于任意節(jié)點(diǎn)恐锦,其到葉節(jié)點(diǎn)尾端的每條路徑都包含相同數(shù)目的黑節(jié)點(diǎn)。(所以是接近平衡的二叉樹(shù))
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末疆液,一起剝皮案震驚了整個(gè)濱河市一铅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌堕油,老刑警劉巖潘飘,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異掉缺,居然都是意外死亡卜录,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)眶明,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)艰毒,“玉大人,你說(shuō)我怎么就攤上這事搜囱〕笄疲” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵犬辰,是天一觀(guān)的道長(zhǎng)嗦篱。 經(jīng)常有香客問(wèn)我,道長(zhǎng)幌缝,這世上最難降的妖魔是什么灸促? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上浴栽,老公的妹妹穿的比我還像新娘荒叼。我一直安慰自己,他們只是感情好典鸡,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布被廓。 她就那樣靜靜地躺著,像睡著了一般萝玷。 火紅的嫁衣襯著肌膚如雪嫁乘。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,262評(píng)論 1 308
  • 那天球碉,我揣著相機(jī)與錄音蜓斧,去河邊找鬼。 笑死睁冬,一個(gè)胖子當(dāng)著我的面吹牛挎春,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播豆拨,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼直奋,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了施禾?” 一聲冷哼從身側(cè)響起脚线,我...
    開(kāi)封第一講書(shū)人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎弥搞,沒(méi)想到半個(gè)月后殉挽,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拓巧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了一死。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肛度。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖投慈,靈堂內(nèi)的尸體忽然破棺而出承耿,到底是詐尸還是另有隱情,我是刑警寧澤伪煤,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布加袋,位于F島的核電站,受9級(jí)特大地震影響抱既,放射性物質(zhì)發(fā)生泄漏职烧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蚀之。 院中可真熱鬧蝗敢,春花似錦、人聲如沸足删。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)失受。三九已至讶泰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間拂到,已是汗流浹背痪署。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谆焊,地道東北人惠桃。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像辖试,于是被迫代替她去往敵國(guó)和親辜王。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

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

  • 本文是我自己在秋招復(fù)習(xí)時(shí)的讀書(shū)筆記罐孝,整理的知識(shí)點(diǎn)呐馆,也是為了防止忘記,尊重勞動(dòng)成果莲兢,轉(zhuǎn)載注明出處哦汹来!如果你也喜歡,那...
    波波波先森閱讀 11,273評(píng)論 4 56
  • 有人半途打道回府,有人堅(jiān)持不懈勇往直前谒兄,創(chuàng)業(yè)摔桦,是一條布滿(mǎn)荊棘的路。而這之中承疲,資產(chǎn)輕與重至關(guān)重要邻耕。 據(jù)相關(guān)資料統(tǒng)計(jì),...
    錢(qián)皓頻道閱讀 253評(píng)論 0 0
  • 陸未離閱讀 169評(píng)論 0 0
  • 培訓(xùn): 強(qiáng)調(diào)原理:能飛就是有羽毛燕鸽,因?yàn)楹芏嗳瞬涣私怿B(niǎo)為什么會(huì)飛的原理兄世,而是看了表現(xiàn),所以制作了羽毛啊研,結(jié)果摔死了 強(qiáng)...
    一條狗的流浪記閱讀 237評(píng)論 0 0