Hashmap在並發環境下,可能出現的問題:
1、多線程put時可能會導致get無限循環,具體表現為CPU使用率100%;
原因:在向HashMap put元素時,會檢查HashMap的容量是否足夠,如果不足,則會新建一個比原來容量大兩倍的Hash表,然后把數組從老的Hash表中遷移到新的Hash表中,遷移的過程就是一個rehash()的過程,多個線程同時操作就有可能會形成循環鏈表,所以在使用get()時,就會出現Infinite Loop的情況
// tranfer()片段
// 這是在resize()中調用的方法,resize()就是HashMap擴容的方法
for (int j = 0; j < src.length; j++) {
Entry e = src[j];
if (e != null) {
src[j] = null;
do {
Entry next = e.next; //假設線程1停留在這里就掛起了,線程2登場
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
} while (e != null);
}
}
線程一:Thread1影響key1
線程二:Thread1影響key2
因為兩條線程的影響,倒是出現循環的情況
出現問題的測試代碼:
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger;
public class MyThread extends Thread {
/**
* 類的靜態變量是各個實例共享的,因此並發的執行此線程一直在操作這兩個變量
* 選擇AtomicInteger避免可能的int++並發問題
*/
private static AtomicInteger ai = new AtomicInteger(0);
//初始化一個table長度為1的哈希表
private static HashMap<Integer, Integer> map = new HashMap<Integer, Integer>(1);
//如果使用ConcurrentHashMap,不會出現類似的問題
// private static ConcurrentHashMap<Integer, Integer> map = new ConcurrentHashMap<Integer, Integer>(1);
public void run() {
while (ai.get() < 100000) { //不斷自增
map.put(ai.get(), ai.get());
ai.incrementAndGet();
}
System.out.println(Thread.currentThread().getName() + "線程即將結束");
}
public static void main(String[] args) {
MyThread t0 = new MyThread();
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
MyThread t4 = new MyThread();
MyThread t5 = new MyThread();
MyThread t6 = new MyThread();
MyThread t7 = new MyThread();
MyThread t8 = new MyThread();
MyThread t9 = new MyThread();
t0.start();
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
t7.start();
t8.start();
t9.start();
}
}
2、多線程put時可能導致元素丟失
原因:當多個線程同時執行addEntry(hash,key ,value,i)時,如果產生哈希碰撞,導致兩個線程得到同樣的bucketIndex去存儲,就可能會發生元素覆蓋丟失的情況
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);
}
建議:
使用Hashtable 類,Hashtable 是線程安全的;
使用並發包下的java.util.concurrent.ConcurrentHashMap,ConcurrentHashMap實現了更高級的線程安全;
或者使用synchronizedMap() 同步方法包裝 HashMap object,得到線程安全的Map,並在此Map上進行操作。