1. 程式人生 > >ConcurrentHashMap原始碼實現

ConcurrentHashMap原始碼實現

concurrentHashmap和HashMap的區別:
concurrentHashmap和HashMap大多數下的使用場景基本一致,但最大的區別就是concurrentHashmap是執行緒安全的HashMap不是,在併發的場景下HashMap存在死迴圈的問題。可以參見部落格https://juejin.im/entry/5884f1a7128fe1006c3b6aac
concurrentHashmap和HashTable的區別:
HashTable是一個執行緒安全的容器類,在HashTable所有方法都是用synchronized關鍵字修飾的,也就是說它是執行緒安全的。但是HashTable的效能十分低下,對於每一個操作都要做加鎖操作,即使操作的是不同的bucket內的Entry也要全域性枷鎖,在高併發場景下效能低下。而concurrentHashmap引入了分段鎖的概念,對於不同Bucket的操作不需要全域性鎖來保證執行緒安全。
一、初始化及變數


對於不同的bucket用不同的鎖來管理,增加了併發性。

/**
     * The default initial capacity for this table,
     * used when not otherwise specified in a constructor.
     * 預設的初始化容量
     */
    static final int DEFAULT_INITIAL_CAPACITY = 16;

    /**
     * The default load factor for this table, used when not
     * otherwise specified in a constructor.
     * 裝載因子
     */
static final float DEFAULT_LOAD_FACTOR = 0.75f; /** * 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. * SEGMENT的最小容量 */ static final int MIN_SEGMENT_TABLE_CAPACITY = 2; /** * The maximum number of segments to allow; used to bound * constructor arguments. Must be power of two less than 1 << 24.SEGMENT的最大容量 */ static final int MAX_SEGMENTS = 1 << 16; // slightly conservative /** * 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;

二、儲存結構
1 每一個segment都是一個特定的HashMap,segment的個數也就是併發數量,table的個數至少是2,而這個table中的每個元素才對應原來HashMap的桶。
2 概念
Segment : 可重入鎖,繼承ReentrantLock 也稱之為桶
HashEntry : 主要儲存鍵值對 可以叫節點

ConcurrentHashMap
3 HashEntry原始碼
volatile保證了多執行緒每次讀的時候可見性。ConcurrentHashMap包含一個Segment陣列,每個Segment包含一個HashEntry陣列,當修改HashEntry陣列採用開鏈法處理衝突,所以它的每個HashEntry元素又是連結串列結構的元素。

    static final class HashEntry<K,V> {
        final int hash;
        final K key;
        volatile V value;
        volatile HashEntry<K,V> next;

        HashEntry(int hash, K key, V value, HashEntry<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }

        /**
         * Sets next field with volatile write semantics.  (See above
         * about use of putOrderedObject.)
         */
        final void setNext(HashEntry<K,V> n) {
            UNSAFE.putOrderedObject(this, nextOffset, n);
        }

        // Unsafe mechanics
        static final sun.misc.Unsafe UNSAFE;
        static final long nextOffset;
        static {
            try {
                UNSAFE = sun.misc.Unsafe.getUnsafe();
                Class k = HashEntry.class;
                nextOffset = UNSAFE.objectFieldOffset
                    (k.getDeclaredField("next"));
            } catch (Exception e) {
                throw new Error(e);
            }
        }
    }

四、構造方法
整個初始化是通過引數initialCapacity(初始容量),loadFactor(增長因子)和concurrencyLevel(併發等級)來初始化segmentShift(段偏移量)、segmentMask(段掩碼)和segment陣列。

 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;//1 併發等級不能超過最大segments的數量
        // Find power-of-two sizes best matching arguments
        int sshift = 0;
        int ssize = 1;
        while (ssize < concurrencyLevel) {
            ++sshift;
            ssize <<= 1;//2 如果你傳入的是15 就是向上取2的4次方倍 也就是16
        }
        this.segmentShift = 32 - sshift;//3 segmentShift和segmentMask在定位segment使用
        this.segmentMask = ssize - 1;//4 
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        int c = initialCapacity / ssize;
        if (c * ssize < initialCapacity)
            ++c;
        int cap = MIN_SEGMENT_TABLE_CAPACITY;
        while (cap < c)
            cap <<= 1;
        //5 create segments and segments[0] 初始化segment
        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;
    }