1. 程式人生 > >探究HashMap線性不安全(三)——死迴圈的產生

探究HashMap線性不安全(三)——死迴圈的產生

內容

  網上很多資料都詳細地講解了HashMap底層的實現,但是講到HashMap的併發操作不是線性安全時,往往一筆帶過:在多個執行緒併發擴容時,會在執行transfer()方法轉移鍵值對時,造成連結串列成環,導致程式在執行get操作時形成死迴圈

​  對於沒有研究過該過程的童鞋,很難費解這句話的含義。下面筆者分四個小節帶著大家共同研究一下JDK1.7和JDK1.8版本下HashMap的線性不安全是怎麼造成的,詳細探究連結串列成環的形成過程。如果對於HashMap底層的put、get操作不清楚,建議先學習參考1中的內容。

適合人群

​  Java進階

參考

正文

​  本節將探究環形連結串列是如何在hashMap查詢時產生死迴圈的。

​  以上節為例,當hashMap物件查詢一個不為空的Key時,會執行getEntry(key)方法。

1 public V get(Object key) {
2         //key為null時,從index為0的連結串列中查詢資料
3         if (key == null)
4             return getForNullKey();
5         //key不為null是查詢資料    
6         Entry<K,V> entry = getEntry(key);
7         //如果未查詢到key對應的值,那麼entry為null,get方法會返回null。
8 return null == entry ? null : entry.getValue(); 9 }

1538216304142

​  getEntry(key)方法會計算Key的hash值,然後通過indexFor(hash, table.length)對hash值取模求得key對應table陣列的下標index。如果這個Key對應的table陣列下標為3,那麼執行緒會在下標3處的環形連結串列上遍歷檢索目標key對應的值。當key對應的鍵值對不存在,執行緒將進入死迴圈,程式碼如下所示:

 1 final Entry<K,V> getEntry(Object key) {
2 //計算key的hash值 3 int hash = (key == null) ? 0 : hash(key); 4 //通過indexFor(hash, table.length)求得key對應的index,然後遍歷index下表的連結串列 5 for (Entry<K,V> e = table[indexFor(hash, table.length)]; 6 e != null; 7 e = e.next) { 8 Object k; 9 //如果連結串列中不存在對應key的鍵值對,且連結串列為環形,那麼當前執行緒將在for語句中產生死迴圈。 10 if (e.hash == hash && 11 ((k = e.key) == key || (key != null && key.equals(k)))) 12 return e; 13 } 14 return null; 15 }

​  至此,JDK1.7中HashMap的線性不安全特性已經論證完畢。下一節,將探究JDK1.8中HashMap的線性不安全特性。