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