成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

為什么 HashMap 會發生數據覆蓋問題

開發 前端
HashMap 之所以發生數據覆蓋的問題,最主要的原因在于它沒有加鎖,所以在多線程環境下會發生數據覆蓋問題。

[[356772]]

本文轉載自微信公眾號「Java極客技術」,作者鴨血粉絲 。轉載本文請聯系Java極客技術公眾號。  

阿粉今天就來談談這個,這個問題在 1.7 版本和 1.8 版本中都有,阿粉分別來說說

在說之前,咱們先要達成一個共識:HashMap 發生數據覆蓋的問題,是在多線程環境 & 擴容下產生的,接下來咱們具體來看

jdk 1.7

  1. void transfer(Entry[] newTable, boolean rehash) {   
  2.        int newCapacity = newTable.length;   
  3.        for (Entry<K,V> e : table) {   
  4.   
  5.            while(null != e) {   
  6.                Entry<K,V> next = e.next;            
  7.                if (rehash) {   
  8.                    e.hash = null == e.key ? 0 : hash(e.key);   
  9.                }   
  10.                int i = indexFor(e.hash, newCapacity);    
  11.                e.next = newTable[i];   
  12.                newTable[i] = e;  // 線程 A 運行到這里時被掛起 
  13.                e = next;   
  14.            }  
  15.        }   
  16.    }   

在擴容時,發生數據覆蓋問題主要核心就是上面的代碼,我們假設一下,剛開始時,結構是這樣的:

現在有兩個線程 A 和 B ,它們都要進行插入操作,首先 A 進行插入操作,經過 Hash 之后就得到了要落到的桶的索引坐標,運行到 newTable[i] = e; 這行代碼時, CPU 時間片用完了,此時線程 A 就停止運行被掛起,這個時候是這個樣子的:

線程 A 被掛起之后,線程 B 被調度得以運行,巧的是,線程 B 經過 Hash 之后得到的要落到的桶索引坐標和線程 A 一樣,此時線程 B 也進行插入操作,線程 B 因為時間片足夠用,所以就成功的將記錄插入到了桶里面:

線程 B 插入成功之后,根據 Java 內存模型,此時主內存中存放的值就是線程 B 運行之后的結果

接下來線程 A 被喚醒,繼續執行插入操作。對于 A 來說,前面的步驟都已經執行過了,所以就不需要再次運行,直接從 newTable[i] = e; 這行代碼開始往下繼續運行即可,線程 A 保存的環境是 e = 12 next = 6 e.next = newTable[i]; 即 newTable[3] = null; ,那么接下來執行 newTable[i] = e; & e = next 也就是 newTable[3] = 12 e = next = 6 執行完畢之后,大概就是這樣:

元素 15 就這么被覆蓋掉了

jdk 1.8

看完 1.7 之后,咱們再來看看 1.8 版本。數據覆蓋主要發生在 put 操作中,下面是 1.8 源碼(阿粉在這里截取了一小部分):

  1. final V putVal(int hash, K key, V value, boolean onlyIfAbsent, 
  2.                 boolean evict) { 
  3.      Node<K,V>[] tab; Node<K,V> p; int n, i; 
  4.      if ((tab = table) == null || (n = tab.length) == 0) 
  5.          n = (tab = resize()).length; 
  6.      if ((p = tab[i = (n - 1) & hash]) == null)     // 如果沒有 hash 碰撞,則直接插入 
  7.          tab[i] = newNode(hash, key, value, null); 
  8.  } 

在上面的代碼中,我們能夠看到,源碼只是判斷了 hash 是否有碰撞,如果沒有就不再做別的檢查進行插入操作

在多線程環境下,如果線程 1 檢查完了 hash 沒有碰撞,要進行插入時, CPU 時間片使用完畢,此時它被掛起,線程 2 開始跑,無巧不成書嘛,此時線程 2 經過 hash 之后得到的值和線程 1 的 hash 值一樣,線程 2 將值插入進去,線程 1 恢復運行,因為前面檢查了 hash 碰撞,此時插入時不再做任何檢查,直接將值插入

那么線程 2 插入的值就被覆蓋掉了

HashMap 之所以發生數據覆蓋的問題,最主要的原因在于它沒有加鎖,所以在多線程環境下會發生數據覆蓋問題

修正一個問題

在 面試官你能不能別問我 HashMap 了? 這篇文章中,阿粉說之所以是 8 轉為紅黑樹,和時間復雜度有關,后來經過一位小伙伴留言才發現阿粉的思路錯了,和泊松分布有關,這一點源碼中也有說明:

  1. Ideally, under random hashCodes, the frequency of nodes in bins follows a Poisson distribution  
  2. with a parameter of about 0.5 on average for the default resizing threshold of 0.75, although  
  3. with a large variance because of resizing granularity. Ignoring variance, the expected  
  4. occurrences of list size k are (exp(-0.5) * pow(0.5, k) / factorial(k)).  
  5. The first values are: 
  6.       0:    0.60653066 
  7.       1:    0.30326533 
  8.       2:    0.07581633 
  9.       3:    0.01263606 
  10.       4:    0.00157952 
  11.       5:    0.00015795 
  12.       6:    0.00001316 
  13.       7:    0.00000094 
  14.       8:    0.00000006 
  15.       more: less than 1 in ten million 

從源碼中可以看到,在負載因子 0.75 ( HashMap 默認)的情況下,單個 hash 槽內元素個數為 8 的概率為 0.00000006,是相當小的一個值了,因此將 7 作為一個分水嶺,等于 7 時不做轉換,大于等于 8 才轉紅黑樹,小于等于 6 才轉鏈表。

哈希攪動和高低 16 位有關系嗎?

有的小伙伴問阿粉,哈希攪動和高低 16 位有關系嗎?

阿粉不太清楚這個有關系是怎樣的一個有關系,但是 16 這個數字的選取,作者肯定也是經過考慮之后再決定的

哈希攪動主要發生在 resize() 這個方法中,我們可以看到源碼中的注釋:

  1. Initializes or doubles table size.   
  2. If null, allocates in  accord with initial capacity target held in field threshold.  
  3. Otherwise, because we are using power-of-two expansion, the elements from each bin must either stay at same indexor move with a power of two offset in the new table

翻譯一下就是:在初始化或者增加表大小時,如果沒有指定,那么就按照初始容量來進行分配。如果指定了,由于使用的是 2 的冪,所以每個 bin 元素也必須保持相同的索引,或者在新表中以 2 的冪偏移

這樣看的話,好像和 16 位有點兒關系

 

責任編輯:武曉燕 來源: Java極客技術
相關推薦

2021-08-19 17:27:41

IT數據中心災難

2024-09-12 09:34:32

2021-12-27 08:24:08

漏洞網絡安全

2020-02-25 10:56:33

云遷移公共云云計算

2011-10-11 15:42:54

大數據數據庫

2023-08-26 07:44:13

系統內存虛擬

2023-01-13 10:36:56

數據中心數據流向

2015-09-25 10:41:48

r語言

2021-03-10 10:40:04

Redis命令Linux

2023-06-27 16:53:50

2020-09-24 09:29:34

人工智能

2015-11-19 00:11:12

2016-01-04 11:03:00

2024-01-18 11:50:28

2015-04-16 10:40:29

2023-04-27 07:40:08

Spring框架OpenAI

2019-02-27 10:18:26

重置Windows 10Windows

2012-12-25 15:19:20

Windows操作系統

2019-03-14 11:00:40

GoLua語言

2020-12-16 19:26:42

IIOTIOT工業物聯網
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 婷婷免费视频 | 欧美日韩国产精品一区二区 | 欧美free性 | 超碰免费在线 | 97伦理最新伦理 | 欧美 日韩 国产 成人 在线 91 | 一区二区精品在线 | 男女精品久久 | 欧美精品三区 | 国产精品99久久久久久www | 久久精品中文 | 日韩中文字幕av | 久久国内精品 | 成人在线观看免费观看 | a级片在线观看 | 午夜网站视频 | 欧美成人一区二免费视频软件 | 五十女人一级毛片 | 亚洲欧美日韩成人在线 | 日韩免费一区二区 | 亚洲免费成人av | 久草视频在线播放 | 中文字幕在线三区 | 97视频网站 | 免费观看成人性生生活片 | 亚洲一区 | 日本在线视频中文字幕 | 一二三四在线视频观看社区 | 91视频国产精品 | 国产视频三区 | 999久久久 | 中文字幕亚洲一区二区三区 | 99久久久久国产精品免费 | 欧美国产日韩一区二区三区 | 国产欧美一区二区三区另类精品 | 日本又色又爽又黄的大片 | 毛片网络 | 97av视频在线观看 | 欧美成人一区二区三区 | 91精品国产一二三 | 欧美一区二区三区视频在线播放 |