ConcurrentHashMap是線程安全,性能出色的Map的線程安全實現,相比較HashMap他是線程安全的,相比較HashTable他的性能優點很是明顯。他的使用很簡單,這裏主要是想要探究一下ConcurrentHashMap的實現原理。
在這裏一共有 個問題須要搞明白。java
帶着這幾個問題咱們來分析一下ConcurrentHashMap的源碼吧。算法
在JDK8(JDK7也是同樣)中ConcurrentHashMap的定義以下:數組
public class ConcurrentHashMap<K,V> extends AbstractMap<K,V> implements ConcurrentMap<K,V>, Serializable {
Java7中ConcurrentHashMap的實現是基於分段鎖實現的。他的底層數據結構仍然是數組+鏈表,與HashTable不一樣的是,ConcurrentHashMap的最外層不是一個大的數組,而是一個Segment數組(分段鎖的實現)。分段鎖減少了鎖的粒度,提升了併發程度。這也是爲何比HashTable效率要高的緣由。
HashTable的源碼其實很簡單,HashTable和HashMap的結構一致,可是每個方法都是用Synchronized來修飾,以保證操做是線程安全的。這樣在多線程的狀況下,只有一個線程獲取鎖操做hashTable中的數據。而CourrentHashMap則不是,它容許最多有segment數組長度個線程同時操做ConcurrentHashMap中的數據。安全
ConcurrentHashMap的總體結構以下(圖片來源:http://www.jasongj.com/java/c...):
數據結構
ConcurrentHashMap的定義:多線程
public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>, Serializable { private static final long serialVersionUID = 7249069246763182397L; /** * 表的默認容量 */ static final int DEFAULT_INITIAL_CAPACITY = 16; /** * 默認擴容因子 */ static final float DEFAULT_LOAD_FACTOR = 0.75f; /** * segments數組的默認長度,爲了能經過按位與的散列算法來定位segments數組的索引,必須保證segments數組的長度是2的N次方 */ static final int DEFAULT_CONCURRENCY_LEVEL = 16; /** * HashEntry最大容量 */ static final int MAXIMUM_CAPACITY = 1 << 30; /** * segment的最小容量 */ static final int MIN_SEGMENT_TABLE_CAPACITY = 2; /** * segment的最大容量 */ static final int MAX_SEGMENTS = 1 << 16; // slightly conservative /** * 重試次數,無鎖的狀況下嘗試兩次 */ static final int RETRIES_BEFORE_LOCK = 2; /** * 散列運算的掩碼,等於ssize-1 */ final int segmentMask; /** * 定位參與散列運算的位數,等於32-sshift */ final int segmentShift; /** * 定義segment數組 */ final Segment<K,V>[] segments;
Segment定義:併發
static final class Segment<K,V> extends ReentrantLock implements Serializable { transient volatile HashEntry<K,V>[] table; transient int count; transient int modCount; //擴容量,默認爲表的容量*加載因子,實際量超過這個值的時候,進行擴容 transient int threshold; //segment中hashEntry的擴容因子 final float loadFactor; }