當前位置:編程學習大全網 - 源碼下載 - hashmap 為什麽線程不安全

hashmap 為什麽線程不安全

壹直以來只是知道HashMap是線程不安全的,但是到底HashMap為什麽線程不安全,多線程並發的時候在什麽情況下可能出現問題?

HashMap底層是壹個Entry數組,當發生hash沖突的時候,hashmap是采用鏈表的方式來解決的,在對應的數組位置存放鏈表的頭結點。對鏈表而言,新加入的節點會從頭結點加入。

javadoc中關於hashmap的壹段描述如下:

此實現不是同步的。如果多個線程同時訪問壹個哈希映射,而其中至少壹個線程從結構上修改了該映射,則它必須 保持外部同步。(結構上的修改是指添加或刪除壹個或多個映射關系的任何操作;僅改變與實例已經包含的鍵關聯的值不是結構上的修改。)這壹般通過對自然封裝該映射的對象進行同步操作來完成。如果不存在這樣的對象,則應該使用 Collections.synchronizedMap 方法來“包裝”該映射。最好在創建時完成這壹操作,以防止對映射進行意外的非同步訪問,如下所示:

Map m = Collections.synchronizedMap(new HashMap(...));

1、

[java] view plain copy

void addEntry(int hash, K key, V value, int bucketIndex) {

Entry<K,V> e = table[bucketIndex];

table[bucketIndex] = new Entry<K,V>(hash, key, value, e);

if (size++ >= threshold)

resize(2 * table.length);

}

在hashmap做put操作的時候會調用到以上的方法。現在假如A線程和B線程同時對同壹個數組位置調用addEntry,兩個線程會同時得到現在的頭結點,然後A寫入新的頭結點之後,B也寫入新的頭結點,那B的寫入操作就會覆蓋A的寫入操作造成A的寫入操作丟失

2、

[java] view plain copy

final Entry<K,V> removeEntryForKey(Object key) {

int hash = (key == null) ? 0 : hash(key.hashCode());

int i = indexFor(hash, table.length);

Entry<K,V> prev = table[i];

Entry<K,V> e = prev;

while (e != null) {

Entry<K,V> next = e.next;

Object k;

if (e.hash == hash &&

((k = e.key) == key || (key != null && key.equals(k)))) {

modCount++;

size--;

if (prev == e)

table[i] = next;

else

prev.next = next;

e.recordRemoval(this);

return e;

}

prev = e;

e = next;

}

return e;

}

刪除鍵值對的代碼如上:

當多個線程同時操作同壹個數組位置的時候,也都會先取得現在狀態下該位置存儲的頭結點,然後各自去進行計算操作,之後再把結果寫會到該數組位置去,其實寫回的時候可能其他的線程已經就把這個位置給修改過了,就會覆蓋其他線程的修改

3、addEntry中當加入新的鍵值對後鍵值對總數量超過門限值的時候會調用壹個resize操作,代碼如下:

[java] view plain copy

void resize(int newCapacity) {

Entry[] oldTable = table;

int oldCapacity = oldTable.length;

if (oldCapacity == MAXIMUM_CAPACITY) {

threshold = Integer.MAX_VALUE;

return;

}

Entry[] newTable = new Entry[newCapacity];

transfer(newTable);

table = newTable;

threshold = (int)(newCapacity * loadFactor);

}

這個操作會新生成壹個新的容量的數組,然後對原數組的所有鍵值對重新進行計算和寫入新的數組,之後指向新生成的數組。

當多個線程同時檢測到總數量超過門限值的時候就會同時調用resize操作,各自生成新的數組並rehash後賦給該map底層的數組table,結果最終只有最後壹個線程生成的新數組被賦給table變量,其他線程的均會丟失。而且當某些線程已經完成賦值而其他線程剛開始的時候,就會用已經被賦值的table作為原始數組,這樣也會有問題。

  • 上一篇:如何從後臺得到webshell
  • 下一篇:求壹個安卓手機統計每天撥出和接入時間的APP或者源代碼
  • copyright 2024編程學習大全網