講清楚ConcurrentHashMap(JDK 1.7)

1、ConcurrentHashMap 鳥瞰圖
一、數據結構

ConcurrentHashMap數據結構在JDK1.7中的實現是二維數組加鏈表的結構,下面是畫出來的鳥瞰圖。數組

ConcurrentHashMap鳥瞰圖

  1. 第一個紅色標記segments數組初始長度是16(如何肯定的後面會提到),同時也是ConcurrentHashMap理想狀態下最大的線程併發數。強調一點,segments是final的因此數組長度在初始化之後是不能改變的。
  2. 第二個紅色標記HashEntry數組初始化長度是2(如何肯定的後面會提到),這也是ConcurrentHashMap實際存儲數據的類。經過造成鏈表在同一個位置上存放多個數據。
  3. 第三個紅色標記ReentrantLock是segment的父類,提供鎖的功能,是ConcurrentHashMap保證線程安全的關鍵。從鳥瞰圖能夠看出,鎖住的是單獨的一個segment,只要多線程狀況下,不是定位到同一個segment就不會形成線程衝突,從而提升多線程的執行效率。
二、屬性

列出來的都是些重要參數,影響了ConcurrentHashMap的初始容量、擴容因子、默認的線程併發量、嘗試次數等等安全

//默認容量。這裏須要說明下,跟HashMap的做用不同,它不是直接控制HashEntry的容量,而是經過運行間接的得出。
static final int DEFAULT_INITIAL_CAPACITY = 16;
//負載因子。
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//默認併發等級。控制segments數組的長度,即ConcurrentHashMap的最大線程併發量。
static final int DEFAULT_CONCURRENCY_LEVEL = 16;
//嘗試次數。某些方法,例如size()等,會用到。具體做用是方法運行到指定次數之後還不是預期的結果就對方法進行加鎖,再運行。
static final int RETRIES_BEFORE_LOCK = 2;
三、方法

這裏就列出幾個有表明性的方法,不涉及到過多的細節,主要想學習設計思路。在講代碼的時候會隱藏掉一些細節,並用註釋代替這段代碼的做用,大部分狀況下均可以忽略它的做用,儘可能讓你們把更多的注意力放在主要代碼上。數據結構

  1. ConcurrentHashMap構造方法
public ConcurrentHashMap(int initialCapacity,
                             float loadFactor, int concurrencyLevel) {
        code_1 //三個參數都必須大於0,不然拋出異常。
        code_2 //concurrencyLevel大於65536時,設爲65536
        // Find power-of-two sizes best matching arguments
        int sshift = 0;//這個不不知道意義是啥
        int ssize = 1;//segments數組長度。
        //每次循環至關於 ssize = 2^n,默認concurrencyLevel=16,那麼ssize=16,sshift=4
        while (ssize < concurrencyLevel) {
            ++sshift;
            ssize <<= 1;
        }
        //這兩個參數的做用後面會提到
        this.segmentShift = 32 - sshift;
        this.segmentMask = ssize - 1;
        code_3 //initialCapacity大於1073741824 時,設爲1073741824
        code_4 //必定的運算得出 cap,這裏只討論 cap=2的默認狀況。感興趣的能夠本身看源碼
        int cap = MIN_SEGMENT_TABLE_CAPACITY;
        //建立 segment[]和初始化segment[0],segment[0]的做用給其餘位置初始化時提供參數。
        Segment<K,V> s0 =
            new Segment<K,V>(loadFactor, (int)(cap * loadFactor),
                             (HashEntry<K,V>[])new HashEntry[cap]);
        Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize];
        UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0]
        this.segments = ss;
    }

總結來講構方法作的就時建立segments[]和初始化 0 位置多線程

  1. put方法
ConcurrentHashMap的
 public V put(K key, V value) {
        Segment<K,V> s;
        code_1// value不能爲空,不然異常
        code_2// 計算key在segments的下標
        int j = XX;
        if ((s = (Segment<K,V>)UNSAFE.getObject          // nonvolatile; recheck
             (segments, (j << SSHIFT) + SBASE)) == null) //  in ensureSegment
            //若是爲空就初始化
            s = ensureSegment(j);
               //使用segment的
        return s.put(key, hash, value, false);
    }

有時間再接着寫把。。。。。。。。。。。併發

相關文章
相關標籤/搜索