ConcurrentHashMap(1.7)分析

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

相關文章
相關標籤/搜索