高併發下,HashMap會產生哪些問題?

HashMap在高併發環境下會產生的問題

HashMap其實並非線程安全的,在高併發的狀況下,會產生併發引發的問題:
好比:java

  • HashMap死循環,形成CPU100%負載
  • 觸發fail-fast

下面逐個分析下出現上述狀況的緣由:安全

HashMap死循環的緣由

HashMap進行存儲時,若是size超過(當前最大容量*負載因子)時候會發生resize,首先看一下resize源代碼:多線程

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方法是真正執行rehash的操做,容易在高併發時發生問題
        transfer(newTable);
        table = newTable;
        threshold = (int)(newCapacity * loadFactor);
    }

而這段代碼中又調用了transfer()方法,而這個方法實現的機制就是將每一個鏈表轉化到新鏈表,而且鏈表中的位置發生反轉,而這在多線程狀況下是很容易形成鏈表迴路,從而發生死循環,咱們看一下他的源代碼:併發

void transfer(Entry[] newTable) {
        Entry[] src = table;
        int newCapacity = newTable.length;
        for (int j = 0; j < src.length; j++) {
            Entry<K,V> e = src[j];
            if (e != null) {
                src[j] = null;
                do {
                    Entry<K,V> next = e.next;
                    int i = indexFor(e.hash, newCapacity);
                    e.next = newTable[i];
                    newTable[i] = e;
                    e = next;
                } while (e != null);
            }
        }
    }

HashMap死循環演示:
假若有兩個線程P一、P2,以及table[]某個節點鏈表爲 a->b->null(a、b是HashMap的Entry節點,保存着Key-Value鍵值對的值)ide

  1. P1先執行,執行完"Entry<K,V> next = e.next;"代碼後,P1發生阻塞或者其餘狀況再也不執行下去,此時e=a,next=b高併發

  2. P1阻塞後P2得到CPU資源開始執行,因爲P1並無執行完transfer(),table 和 threshold仍爲原來的值,P2依舊會進行resize操做,而且P2順利執行完resize()方法,假設a、b節點仍然rehash到newTable[](注意,P1和P2中newTable[]不是同一個)中同一個節點鏈表中,則新的節點鏈表爲 b->a->null
    transfer(newTable); //P1阻塞在transfer方法中,沒有執行到下邊對 table 和 threshold 從新賦值的操做 table = newTable; threshold = (int)(newCapacity * loadFactor);
  3. P1又繼續執行"Entry<K,V> next = e.next;"以後的代碼,則newTable[i]的節點鏈表變化過程爲:
    • 第一次while循環,newTable[i]=a,鏈表爲:b->a->null;此時e=b;
    • 進入第二次循環,newTable[i]=b,鏈表爲:b->a->a; 此時a<->a出現迴路,e=a, while(e!=null)死循環

觸發fail-fast

一個線程利用迭代器迭代時,另外一個線程作插入刪除操做,形成迭代的fast-fail。ui

public class TestFailFast {
    
    private static final String USER_NAME_PREFIX = "User-";
    // Key: User Name, Value: User Age
    private static Map<String, Integer> userMap = new HashMap<>();
    
    // ThreadA 用於向HashMap添加元素
    static class ThreadA implements Runnable {
        @Override
        public void run() {
            System.out.println("ThreadA starts to add user.");
            for (int i = 1; i < 100000; i++) {
                userMap.put(USER_NAME_PREFIX+i, i%100);
            }
            System.out.println("ThreadA done.");
        }
    }
    
    // ThreadB 用於遍歷HashMap中元素輸出
    static class ThreadB implements Runnable {
        @Override
        public void run() {
            System.out.println("ThreadB starts to iterate.");
            for (Map.Entry<String, Integer> user : userMap.entrySet()) {
                System.out.println("UserName=" + user.getKey()
                    + ", UserAge=" + user.getValue());
            }
            System.out.println("ThreadB done.");
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        Thread threadA = new Thread(new ThreadA());
        Thread threadB = new Thread(new ThreadB());
    
        threadA.start();
        threadB.start();
  
        threadA.join();
        threadB.join();
        System.exit(0);
    }
}

運行結果:拋出ConcurrentModificationException.net

ThreadA starts to add user.
ThreadB starts to iterate.
Exception in thread "Thread-1" java.util.ConcurrentModificationException
    at java.util.HashMap$HashIterator.nextNode(HashMap.java:1437)
    at java.util.HashMap$EntryIterator.next(HashMap.java:1471)
    at java.util.HashMap$EntryIterator.next(HashMap.java:1469)
    at concurrent.TestFailFast$ThreadB.run(TestFailFast.java:33)
    at java.lang.Thread.run(Thread.java:748)
ThreadA done.

總結

HashMap並不是線程安全,因此在多線程狀況下,應該首先考慮用ConcurrentHashMap,避免悲劇的發生。線程

參考資料:

https://blog.csdn.net/chenxuegui1234/article/details/39646041
https://blog.csdn.net/u011716215/article/details/78601916code

相關文章
相關標籤/搜索