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


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

 

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

  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的數據結構如下:

可以看出,相對 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 個線程的並發訪問,從而大大提高了並發訪問的效率。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM