Java 非線程安全的HashMap如何在多線程中使用

Java 非線程安全的HashMap如何在多線程中使用

 

HashMap 是非線程安全的。在多線程條件下,容易致使死循環,具體表現爲CPU使用率100%。所以多線程環境下保證 HashMap 的線程安全性,主要有以下幾種方法:java

  1. 使用 java.util.Hashtable 類,此類是線程安全的。
  2. 使用 java.util.concurrent.ConcurrentHashMap,此類是線程安全的。
  3. 使用 java.util.Collections.synchronizedMap() 方法包裝 HashMap object,獲得線程安全的Map,並在此Map上進行操做。
  4. 本身在程序的關鍵代碼段加鎖,保證多線程安全(不推薦)

 

接下來分析上面列舉的幾種方法實現併發安全的 HashMap 的原理:

(一)java.util.Hashtable類:數組

查看該類的源碼安全

public synchronized V get(Object key) {  
    …… //具體的實現省略,請參考 jdk實現  
}  
  
public synchronized V put(K key, V value) {  
    …… //具體的實現省略,請參考 jdk實現  
}  
  
public synchronized V remove(Object key) {  
    …… //具體的實現省略,請參考 jdk實現  
}  

     上面是 Hashtable 類提供的幾個主要方法,包括 get(),put(),remove() 等。注意到每一個方法自己都是 synchronized 的,不會出現兩個線程同時對數據進行操做的狀況,所以保證了線程安全性,可是也大大的下降了執行效率。所以是不推薦的。數據結構

 

(二)使用 java.util.concurrent.ConcurrentHashMap 類:多線程

該類是 HashMap 的線程安全版,與 Hashtable 相比, ConcurrentHashMap 不只保證了訪問的線程安全性,並且在效率上有較大的提升。併發

ConcurrentHashMap的數據結構以下:spa

能夠看出,相對 HashMap 和 Hashtable, ConcurrentHashMap 增長了Segment 層,每一個Segment 原理上等同於一個 Hashtable, ConcurrentHashMap 等同於一個 Segment 的數組。下面是 ConcurrentHashMap 的 put 和 get 方法:線程

final Segment<K,V> segmentFor(int hash) {  
    return segments[(hash >>> segmentShift) & segmentMask];  
}  
  
public V put(K key, V value) {  
    if (value == null)  
        throw new NullPointerException();  
    int hash = hash(key.hashCode());  
    return segmentFor(hash).put(key, hash, value, false);  
}  
  
public V get(Object key) {  
    int hash = hash(key.hashCode());  
    return segmentFor(hash).get(key, hash);  
}  

向 ConcurrentHashMap 中插入數據(put) 或者 讀取數據(get),首先都要將相應的 Key 映射到對應的 Segment,所以不用鎖定整個類, 只要對單個的 Segment 操做進行上鎖操做就能夠了。理論上若是有 n 個 Segment,那麼最多能夠同時支持 n 個線程的併發訪問,從而大大提升了併發訪問的效率。code

相關文章
相關標籤/搜索