首先hashmap在多個執行緒同時對其操作的時候造成的髒讀很統一理解,比如乙個執行緒a對hashmap進行讀操作,乙個執行緒b對hashmap就行寫操作。執行緒b先進入put方法中,此時還沒有寫資料的時候執行緒a輪轉執行,並一直執行到結束,假設執行取到資料為條,這時執行緒b繼續執行新增了一條資料。那麼最後hashmap的資料是4條,但是執行緒a只讀取了三條。
不論是讀或者寫,或者修改,在多執行緒環境下都會出現資料的不一致的問題。但是更為嚴重的是hashmap會在某些時候讓程式直接陷入死迴圈。
我們都知道hashmap初始容量大小為16,一般來說,當有資料要插入時,都會檢查容量有沒有超過設定的thredhold,如果超過,需要增大hash表的尺寸,但是這樣一來,整個hash表裡的元素都需要被重算一遍。這叫rehash,這個成本相當的大
首先是單執行緒環境下rehash過程如下所示
1、假設我們的hash演算法是簡單的key mod一下表的大小(即陣列的長度)。
2、最上面是old hash表,其中hash表的size=2,所以key=3,5,7在mod 2 以後都衝突在table[1]這個位置上了。
3、接下來hash表擴容,resize=4,然後所有的重新進行雜湊分布,過程如下:
在單執行緒情況下,一切看起來都很美妙,擴容過程也相當順利。接下來看下併發情況下的擴容。
1、首先假設我們有兩個執行緒,分別用紅色和藍色標註了。
2、擴容部分的源**:
4、接著cpu切換到執行緒一上來,執行8-14行**,首先安置3這個entry:
這裡需要注意的是:執行緒二已經完成執行完成,現在table裡面所有的entry都是最新的,就是說7的next是3,3的next是null;現在第一次迴圈已經結束,3已經安置妥當。看看接下來會發生什麼事情:
1、e=next=7;
2、e!=null,迴圈繼續
3、next=e.next=3
4、e.next 7的next指向3
5、放置7這個entry,現在如圖所示:
放置7之後,接著執行**:
1、e=next=3;
2、判斷不為空,繼續迴圈
3、next= e.next 這裡也就是3的next 為null
4、e.next=7,就3的next指向7.
5、放置3這個entry,此時的狀態如圖:
這個時候其實就出現了死迴圈了,3移動節點頭的位置,指向7這個entry;在這之前7的next同時也指向了3這個entry。
**接著往下執行,e=next=null,此時條件判斷會終止迴圈。這次擴容結束了。但是後續如果有查詢(無論是查詢的迭代還是擴容),都會hang死在table這個位置上。現在回過來看文章開頭的那個demo,就是掛死在擴容階段的transfer這個方法上面。
hashMap的執行緒不安全
hashmap是非執行緒安全的,表現在兩種情況下 1 擴容 t1執行緒對map進行擴容,此時t2執行緒來讀取資料,原本要讀取位置為2的元素,擴容後此元素位置未必是2,則出現讀取錯誤資料。2 hash碰撞 兩個執行緒新增元素發生hash碰撞,都要將此元素新增到鍊錶的頭部,則會發生資料被覆蓋。詳情 ha...
HashMap 執行緒不安全的原因
hashmap執行緒安全的問題,在各大面試中都會被問到,屬於常考熱點題目。雖然大部分讀者都了解它不是執行緒安全的,但是再深入一些,問它為什麼不是執行緒安全的,仔細說說原理,用圖畫出一種非執行緒安全的情況?1.8之後又做了什麼改善了這點?很多讀者可能一時想不出很好的答案。我們關注下面的 void tr...
gets 函式的不安全性
在linux下編譯c檔案時出現 warning the gets function is dangerous and should not be used問題在於gets 函式在獲取輸入時,不會對輸入有檢查,如果無限輸入會造成棧空間溢位,在程式返回時,不能正常的找到返回位址,程式將發生不可 行為。使...