看ConcurrentHashMap下幾個屬性:java
/** * The default concurrency level for this table, used when not * otherwise specified in a constructor. * 默認的分段鎖個數 */ static final int DEFAULT_CONCURRENCY_LEVEL = 16; /** * 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; /** * 嘗試獲取鎖的次數 * Number of unsynchronized retries in size and containsValue * methods before resorting to locking. This is used to avoid * unbounded retries if tables undergo continuous modification * which would make it impossible to obtain an accurate result. */ static final int RETRIES_BEFORE_LOCK = 2; /** * The segments, each of which is a specialized hash table. * 分段鎖數組 */ final Segment<K,V>[] segments;
/** * Segments are specialized versions of hash tables. This * Segments 也是一個定製的hashtable * subclasses from ReentrantLock opportunistically, just to * 同時他也是 ReentrantLock的子類 * simplify some locking and avoid separate construction. */ static final class Segment<K,V> extends ReentrantLock implements Serializable { ..... }
//以put爲例分析 /** * Maps the specified key to the specified value in this table. * Neither the key nor the value can be null. * key 和 value 都不能爲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 */ @SuppressWarnings("unchecked") public V put(K key, V value) { Segment<K,V> s; if (value == null) throw new NullPointerException(); int hash = hash(key);//先經過key求hash,再獲取當前hash在哪一個分段鎖內,這些全是位操做,比較煩,也能分析透,還有UNSAFE類的使用比較多。UNSAFE是高危操做類,可是高效,功能強大。 int j = (hash >>> segmentShift) & segmentMask; if ((s = (Segment<K,V>)UNSAFE.getObject // nonvolatile; recheck (segments, (j << SSHIFT) + SBASE)) == null) // in ensureSegment s = ensureSegment(j);//沒找到合適分段,調用ensureSegment() 看下面, return s.put(key, hash, value, false);//調用的分段鎖Segment的put方法 } /** * Returns the segment for the given index, creating it and * recording in segment table (via CAS) if not already present. * 返回給定的索引的分段,不存在就建立一個。 * @param k the index * @return the segment */ @SuppressWarnings("unchecked") private Segment<K,V> ensureSegment(int k) { final Segment<K,V>[] ss = this.segments; long u = (k << SSHIFT) + SBASE; // raw offset Segment<K,V> seg; if ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u)) == null) {//經過索引沒取到分段,建立分段 Segment<K,V> proto = ss[0]; // use segment 0 as prototype //老是以ss[0]爲例 這點寫死了 int cap = proto.table.length; float lf = proto.loadFactor; int threshold = (int)(cap * lf); HashEntry<K,V>[] tab = (HashEntry<K,V>[])new HashEntry[cap]; if ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u)) == null) { // recheck//再檢查一次,有沒有合適的分段 Segment<K,V> s = new Segment<K,V>(lf, threshold, tab);//建立分段 while ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u)) == null) { //再檢查一次,有沒有合適的分段 第三次檢查, if (UNSAFE.compareAndSwapObject(ss, u, null, seg = s))//最後用cas方法,把新建的分段放到分段數組中 break; } } } return seg; } // 關鍵看下Segment類 /** * Segments are specialized versions of hash tables. This * subclasses from ReentrantLock opportunistically, just to * simplify some locking and avoid separate construction. * 它是ReentrantLock的子類 */ static final class Segment<K,V> extends ReentrantLock implements Serializable {} //看下Segment的put方法 final V put(K key, int hash, V value, boolean onlyIfAbsent) { HashEntry<K,V> node = tryLock() ? null : scanAndLockForPut(key, hash, value);//先用tryLock()獲取鎖,若是成功node=null ,不然進入scanAndLockForPut(key, hash, value)方法,看下面scanAndLockForPut方法 V oldValue; try { HashEntry<K,V>[] tab = table; int index = (tab.length - 1) & hash; HashEntry<K,V> first = entryAt(tab, index); for (HashEntry<K,V> e = first;;) { if (e != null) {//key 的hash位置有值了 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);//擴容 *2的大小 else setEntryAt(tab, index, node);。 ++modCount; count = c; oldValue = null; break; } } } finally { unlock();//釋放鎖 } return oldValue; } /** * Scans for a node containing given key while trying to * acquire lock, creating and returning one if not found. Upon * return, guarantees that lock is held. UNlike in most * methods, calls to method equals are not screened: Since * traversal speed doesn't matter, we might as well help warm * up the associated code and accesses as well. * 經過key找對應node.沒有就建立一個。 * @return a new node if key not found, else null */ private HashEntry<K,V> scanAndLockForPut(K key, int hash, V value) { HashEntry<K,V> first = entryForHash(this, hash); HashEntry<K,V> e = first; HashEntry<K,V> node = null; int retries = -1; // negative while locating node while (!tryLock()) {//沒有獲取鎖,重試 HashEntry<K,V> f; // to recheck first below if (retries < 0) { if (e == null) { if (node == null) // speculatively create node node = new HashEntry<K,V>(hash, key, value, null); retries = 0; } else if (key.equals(e.key)) retries = 0; else e = e.next; } else if (++retries > MAX_SCAN_RETRIES) {//大於最大嘗試次數 lock();//等待,阻塞鎖 break; } else if ((retries & 1) == 0 && (f = entryForHash(this, hash)) != first) { e = first = f; // re-traverse if entry changed retries = -1; } } return node; } /** get 方法不加鎖 * Returns the value to which the specified key is mapped, * or {@code null} if this map contains no mapping for the key. * * <p>More formally, if this map contains a mapping from a key * {@code k} to a value {@code v} such that {@code key.equals(k)}, * then this method returns {@code v}; otherwise it returns * {@code null}. (There can be at most one such mapping.) * * @throws NullPointerException if the specified key is null */ public V get(Object key) { Segment<K,V> s; // manually integrate access methods to reduce overhead HashEntry<K,V>[] tab; int h = hash(key); long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE; if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null && (tab = s.table) != null) { for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE); e != null; e = e.next) { K k; if ((k = e.key) == key || (e.hash == h && key.equals(k))) return e.value; } } return null; }