1. 先來了解ConcurrentHashMap中的幾個成員,固然大多數與HashMap中的類似,咱們只看獨有的成員node
/** * The default concurrency level for this table, used when not * otherwise specified in a constructor. */ static final int DEFAULT_CONCURRENCY_LEVEL = 16; //默認的併發級別
/**
* The maximum capacity, used if a higher value is implicitly
* specified by either of the constructors with arguments. MUST
* be a power of two <= 1<<30 to ensure that entries are indexable
* using ints.
*/
static final int MAXIMUM_CAPACITY = 1 << 30; //最大容量
/**
* The minimum capacity for per-segment tables. Must be a power
* of two, at least two to avoid immediate resizing on next use
* after lazy construction.
*/
static final int MIN_SEGMENT_TABLE_CAPACITY = 2; //每一個Segement中的桶的數量
/**
* The maximum number of segments to allow; used to bound
* constructor arguments. Must be power of two less than 1 << 24.
*/
static final int MAX_SEGMENTS = 1 << 16; // slightly conservative //容許的最大的Segement的數量
/**
* Mask value for indexing into segments. The upper bits of a
* key's hash code are used to choose the segment.
*/
final int segmentMask; //掩碼,用來定位segements數組的位置
/**
* Shift value for indexing within segments.
*/
final int segmentShift; //偏移量,用來確認hash值的有效位
/**
* The segments, each of which is a specialized hash table.
*/
final Segment<K,V>[] segments; //至關於多個HashMap組成的數組
static final class Segment<K,V> extends ReentrantLock implements Serializable { //內部類Segment,繼承了ReentrantLock,有鎖的功能 /** * The maximum number of times to tryLock in a prescan before * possibly blocking on acquire in preparation for a locked * segment operation. On multiprocessors, using a bounded * number of retries maintains cache acquired while locating * nodes. */ static final int MAX_SCAN_RETRIES = Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1; /** * The per-segment table. Elements are accessed via * entryAt/setEntryAt providing volatile semantics. */ transient volatile HashEntry<K,V>[] table; //每一個Segement內部都有一個table數組,至關於每一個Segement都是一個HashMap transient int count; //這些參數與HashMap中的參數功能相同 transient int modCount; transient int threshold; final float loadFactor; Segment(float lf, int threshold, HashEntry<K,V>[] tab) { this.loadFactor = lf; this.threshold = threshold; this.table = tab; } final V put(K key, int hash, V value, boolean onlyIfAbsent) { //向Segement中添加一個元素 }
2. 構造函數算法
@SuppressWarnings("unchecked") public ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) { if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0) //參數校驗 throw new IllegalArgumentException(); if (concurrencyLevel > MAX_SEGMENTS) concurrencyLevel = MAX_SEGMENTS; // Find power-of-two sizes best matching arguments int sshift = 0; int ssize = 1; //計算segement數組的大小,而且爲2的倍數,默認狀況下concurrentyLevel爲16,那麼ssize也爲16 while (ssize < concurrencyLevel) { ++sshift; //ssize每次進行左移運算,所以sshift能夠看作是ssize參數左移的位數 ssize <<= 1; }
this.segmentShift = 32 - sshift; //segement偏移量 this.segmentMask = ssize - 1; //因爲ssize爲2的倍數,因此sengemnt爲全1的,用來定位segement數組的下標 if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY;
int c = initialCapacity / ssize; //計算每一個Segement中桶的數量 if (c * ssize < initialCapacity) ++c; int cap = MIN_SEGMENT_TABLE_CAPACITY; while (cap < c) cap <<= 1; // create segments and segments[0] Segment<K,V> s0 = //初始化第一個segement new Segment<K,V>(loadFactor, (int)(cap * loadFactor), (HashEntry<K,V>[])new HashEntry[cap]); Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize]; //新建segements數組,並將s0賦值 UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0] this.segments = ss; }
3 . 咱們來看put()方法數組
@SuppressWarnings("unchecked") public V put(K key, V value) { Segment<K,V> s; if (value == null) //ConcurrentHashMap中value不能爲空 throw new NullPointerException(); int hash = hash(key); //獲取到key的hash值 int j = (hash >>> segmentShift) & segmentMask; //定位到某個segement位置 if ((s = (Segment<K,V>)UNSAFE.getObject //從上面的構造方法中咱們知道,segments數組只有0位置的segment被初始化了,所以這裏須要去檢測計算出的位置的segment是否被初始化
因爲是併發容器,因此使用UNSAFE中的方法
(segments, (j << SSHIFT) + SBASE)) == null) // in ensureSegment s = ensureSegment(j); return s.put(key, hash, value, false); //將元素插入到定位的Segement中 }
final V put(K key, int hash, V value, boolean onlyIfAbsent) { //segement中的put()方法 HashEntry<K,V> node = tryLock() ? null : //獲取鎖,若獲取不到鎖,則縣建立節點並返回 scanAndLockForPut(key, hash, value); V oldValue; try { HashEntry<K,V>[] tab = table; //以後的算法就與HashMap中類似了 int index = (tab.length - 1) & hash; HashEntry<K,V> first = entryAt(tab, index); for (HashEntry<K,V> e = first;;) { if (e != null) { K k; if ((k = e.key) == key || (e.hash == hash && key.equals(k))) { oldValue = e.value; if (!onlyIfAbsent) { e.value = value; ++modCount; } break; } e = e.next; } else { if (node != null) node.setNext(first); else node = new HashEntry<K,V>(hash, key, value, first); int c = count + 1; if (c > threshold && tab.length < MAXIMUM_CAPACITY) rehash(node); else setEntryAt(tab, index, node); ++modCount; count = c; oldValue = null; break; } } } finally { unlock(); //釋放鎖 } return oldValue; }
4. 來具體看一下SegementMask與SegmentShift這兩個變量時怎麼使用的?併發
int sshift = 0; int ssize = 1; //計算segement數組的大小,而且爲2的倍數,默認狀況下concurrentyLevel爲16,那麼ssize也爲16 while (ssize < concurrencyLevel) { ++sshift; //ssize每次進行左移運算,所以sshift能夠看作是ssize參數左移的位數 ssize <<= 1; } this.segmentShift = 32 - sshift; //segement偏移量 this.segmentMask = ssize - 1; //因爲ssize爲2的倍數,因此sengemnt爲全1的,用來定位segement數組的下標
上面是構造函數中計算這兩個變量的代碼。less
咱們假設concurrencyLevel爲默認值16,那麼通過計算獲得,ssize = 16,sshift = 4,segmentShift = 28, segementMask = 15ssh
因爲ssize爲segements數組的大小,咱們能夠發現,當 n 與 segmentMask按位與時候正好能夠獲得<=15的數組,正是segements數組的下標。函數
int j = (hash >>> segmentShift) & segmentMask; //定位到某個segement位置
而segementShift的做用在於縮小hash值的範圍,咱們並不須要使用hash值全部的位,經過上面的數據,當hash值右移28位後正好能夠獲得有效計算的位數(4位),所以上面構造函數中的sshift也ui
能夠表示計算segements數組時的有效位數。this