Hashmap在併發環境下,可能出現的問題:
一、多線程put時可能會致使get無限循環,具體表現爲CPU使用率100%;
緣由:在向HashMap put元素時,會檢查HashMap的容量是否足夠,若是不足,則會新建一個比原來容量大兩倍的Hash表,而後把數組從老的Hash表中遷移到新的Hash表中,遷移的過程就是一個rehash()的過程,多個線程同時操做就有可能會造成循環鏈表,因此在使用get()時,就會出現Infinite Loop的狀況java
// 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(); } }
二、多線程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上進行操做。多線程