hashmap底層是乙個entry陣列,當發生hash衝突的時候,hashmap是採用鍊錶的方式來解決的,在對應的陣列位置存放鍊錶的頭結點。對鍊錶而言,新加入的節點會從頭結點加入。
我們來分析一下多執行緒訪問:
1.在hashmap做put操作的時候會呼叫下面方法:
//在hashmap做put操作的時候會呼叫到以上的方法。現在假如a執行緒和b執行緒同時對同乙個陣列位置呼叫addentry,兩個執行緒會同時得到現在的頭結點,然後a寫入新的頭結點之後,b也寫入新的頭結點,那b的寫入操作就會覆蓋a的寫入操作造成a的寫入操作丟失新增entry。將「key-value」插入指定位置,bucketindex是位置索引。
void addentry(int hash, k key, v value, int
bucketindex)
2.刪除鍵值對的**
//刪除「鍵為key」的元素當多個執行緒同時操作同乙個陣列位置的時候,也都會先取得現在狀態下該位置儲存的頭結點,然後各自去進行計算操作,之後再把結果寫會到該陣列位置去,其實寫回的時候可能其他的執行緒已經就把這個位置給修改過了,就會覆蓋其他執行緒的修改。final entryremoveentryforkey(object key)
prev =e;
e =next;
}
return
e;
}
3.addentry中當加入新的鍵值對後鍵值對總數量超過門限值的時候會呼叫乙個resize操作,**如下:
//這個操作會新生成乙個新的容量的陣列,然後對原陣列的所有鍵值對重新進行計算和寫入新的陣列,之後指向新生成的陣列。重新調整hashmap的大小,newcapacity是調整後的容量
void resize(int
newcapacity)
//新建乙個hashmap,將「舊hashmap」的全部元素新增到「新hashmap」中,
//然後,將「新hashmap」賦值給「舊hashmap」。
entry newtable = new
entry[newcapacity];
transfer(newtable);
table =newtable;
threshold = (int)(newcapacity *loadfactor);
}
當多個執行緒同時檢測到總數量超過門限值的時候就會同時呼叫resize操作,各自生成新的陣列並rehash後賦給該map底層的陣列table,結果最終只有最後乙個執行緒生成的新陣列被賦給table變數,其他執行緒的均會丟失。而且當某些執行緒已經完成賦值而其他執行緒剛開始的時候,就會用已經被賦值的table作為原始陣列,這樣也會有問題。
HashMap為什麼是執行緒不安全的
jdk1.7中hashmap的transfer函式如下 void transfer entry newtable,boolean rehash int i indexfor e.hash,newcapacity e.next newtable i newtable i e e next 此函式tra...
hashmap為什麼不安全
第一點多執行緒同時put的時候 在某一時刻同時操作hashmap並執行put操作,而有大於兩個key的hash值相同,如圖中a1 a2,這個時候需要解決碰撞衝突,而解決衝突的辦法上面已經說過,對於鍊錶的結構在這裡不再贅述,暫且不討論是從鍊錶頭部插入還是從尾部初入,這個時候兩個執行緒如果恰好都取到了對...
53 HashMap 為什麼執行緒不安全?
小景哥哥部落格 jdk7 存在死迴圈和資料丟失問題。資料丟失 死迴圈 擴容時resize呼叫transfer使用頭插法遷移元素,雖然 newtable 是區域性變數,但原先 table 中的 entry 鍊錶是共享的,問題根源是 entry 的 next 指標併發修改,某執行緒還沒有將 table ...