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;
    }

参考文献
https://juejin.im/entry/5884f1a7128fe1006c3b6aac
http://www.blogjava.net/DLevin/archive/2013/10/18/405030.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值