關於HashMap為什么是線程不安全的原因


原因:

我們知道hashmap的擴容因子是0.75,如果hashmap的數組長度已經使用了75%就會引起擴容,會新申請一個長度為原來兩倍的桶數組,

然后將原數組的元素重新映射到新的數組中,原有數據的引用會逐個被置為null。就是在resize()擴容的時候會造成線程不安全。

另外當一個新節點想要插入hashmap的鏈表時,在jdk1.8之前的版本是插在頭部,在1.8后是插在尾部。

那么hashmap什么時候進行擴容呢?當hashmap中的元素個數超過數組大小*loadFactor時,就會進行數組擴容,loadFactor的默認值為0.75,也就是說,默認情況下,數組大小為16,

那么當hashmap中元素個數超過16*0.75=12的時候,就把數組的大小擴展為2*16=32,即擴大一倍,然后重新計算每個元素在數組中的位置,而這是一個非常消耗性能的操作,

所以如果我們已經預知hashmap中元素的個數,那么預設數組的大小能夠有效的提高hashmap的性能。

 

比如:

我們有1000個元素new HashMap(1000), 但是理論上來講new HashMap(1024)更合適,不過上面annegu已經說過,即使是1000,hashmap也自動會將其設置為1024。

但是new HashMap(1024)還不是更合適的,因為0.75*1024 < 1000, 也就是說為了讓0.75 * size > 1000, 我們必須這樣new HashMap(2048)才最合適,避免了resize的問題。

不安全原因:

(1)在put的時候,因為該方法不是同步的,假如有兩個線程A,B它們的put的key的hash值相同,不論是從頭插入還是從尾插入,假如A獲取了插入位置為x,

         但是還未插入,此時B也計算出待插入位置為x,則不論AB插入的先后順序肯定有一個會丟失;

(2)在擴容的時候,jdk1.8之前是采用頭插法,當兩個線程同時檢測到hashmap需要擴容,在進行同時擴容的時候有可能會造成鏈表的循環,

         主要原因就是,采用頭插法,新鏈表與舊鏈表的順序是反的,在1.8后采用尾插法就不會出現這種問題,同時1.8的鏈表長度如果大於8就會轉變成紅黑樹。




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM