預備知識:Java HashMap and HashSet 的實現機制
由預備知識可以知道hashmap 的儲存結構為:
(映像來自http://www.ibm.com/developerworks/cn/java/j-lo-hash/)
也是說:一個hashmap 內部含有一個Entity 類行的數組,這個數組中的元素都是Entity。實際上我們放入map 中的key 和 value 就對應一個Entity 對象,這個Entity 對象包含一個key、value、hashcode(key 的)和一個Entity 的引用,通過這個引用,Entity 可以形成一個鏈表。在圖中,藍色矩形方框代表數組,橙色橢圓代表Entity 對象。
注意HashMap 類不是安全執行緒的。
ConcurrentMap 主要的子類是ConcurrentHashMap。
原理:一個ConcurrentHashMap 由多個segment 組成,每個segment 包含一個Entity 的數組。這裡比HashMap 多了一個segment 類。該類繼承了ReentrantLock 類,所以本身是一個鎖。當多線程對ConcurrentHashMap 操作時,不是完全鎖住map, 而是鎖住相應的segment 。這樣提高了並發效率。
建構函式的分析:
/** * Creates a new, empty map with a default initial capacity (16), * load factor (0.75) and concurrencyLevel (16). */ public ConcurrentHashMap() { this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL); }
這是ConcurrentHashMap 的無參建構函式,可以看到預設大小為16,負載因子0.75,並發層級為16.
Put 函數的分析:
/** * Maps the specified key to the specified value in this table. * Neither the key nor the value can be null. * * <p> The value can be retrieved by calling the <tt>get</tt> method * with a key that is equal to the original key. * * @param key key with which the specified value is to be associated * @param value value to be associated with the specified key * @return the previous value associated with <tt>key</tt>, or * <tt>null</tt> if there was no mapping for <tt>key</tt> * @throws NullPointerException if the specified key or value is null */ 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); }
可以看出通過hash() 函數得到key 的雜湊值,在得到相應的segment,在通過segment 儲存Entity。
同時為了避免“檢測再修改”(有條件安全執行緒參見[2])等並發問題,ConcurrentHashMap 提供了putIfAbsent(K key, V value) 方法,當key 不存在時,添加。(key 的存在靠兩個條件,一個是key的hashcode方法,另外一個是key 的equal 方法)
優點:由於對對應segment 加鎖,而不是鎖定整個map,並發性得到了提高。能夠直接提高插入、檢索以及移除操作的延展性。
缺點:當遍曆map 中的元素時,需要擷取所有的segment 的鎖,使用遍曆時慢。鎖的增多,佔用了系統的資源。使得對整個集合進行操作的一些方法(例如 size() 或 isEmpty() )的實現更加困難,因為這些方法要求一次獲得許多的鎖,並且還存在返回不正確的結果的風險。
參考資料:
1 通過分析 JDK 原始碼研究 Hash 儲存機制
2 Java 理論與實踐: 並發集合類
3 Java 理論與實踐: 構建一個更好的 HashMap