美文网首页
ConcurrentHashMap源码分析(01)-构造方法

ConcurrentHashMap源码分析(01)-构造方法

作者: juconcurrent | 来源:发表于2018-12-10 16:50 被阅读4次

    前言

    ConcurrentHashMap作为并发工具集里面的一员,扮演着极其重要的角色。它支持HashMap的绝大多数功能,并且保证线程安全。为了线程安全,它内部的实现用到了锁、CAS和自旋等不同于HashMap的操作。

    ConcurrentHashMap在jdk8中的实现,又有别于jdk7及以前的版本。在jdk7中,ConcurrentHashMap的实现是基于Segment分段锁的方式。而jdk8摒弃了分段锁技术,采用了性能更优的方式。

    【注意】:本系列文章分析的ConcurrentHashMap是基于jdk8版本的,若读者需要jdk7及以前版本的,请自行查阅google。

    重要的成员变量

    /**
     * The array of bins. Lazily initialized upon first insertion.
     * Size is always a power of two. Accessed directly by iterators.
     */
    transient volatile Node<K,V>[] table;
    
    /**
     * Base counter value, used mainly when there is no contention,
     * but also as a fallback during table initialization
     * races. Updated via CAS.
     */
    private transient volatile long baseCount;
    
    /**
     * Table initialization and resizing control.  When negative, the
     * table is being initialized or resized: -1 for initialization,
     * else -(1 + the number of active resizing threads).  Otherwise,
     * when table is null, holds the initial table size to use upon
     * creation, or 0 for default. After initialization, holds the
     * next element count value upon which to resize the table.
     */
    private transient volatile int sizeCtl;
    

    tablebaseCountsizeCtl都是volatile类型的,说明了其可见性和有序性。

    table和HashMap里面的类似,类似一个个的槽,每个槽的hash是不同的,同一个槽的hash相同。如果槽里面的元素个数不超过8个,则槽里面的元素使用链表。如果槽里面的元素个数超过8个,则槽里面的元素将转换为红黑树。

    baseCount表示容器的大小。

    sizeCtl用于table的初始化和扩容,有几种情况。

    1. -1,表示table正在初始化
    2. -N,表示有N-1个线程正在进行扩容操作
    3. 其他情况
      1. 如果table未初始化,表示table需要初始化的大小
      2. 如果table初始化完成,表示table的容量,默认是table大小的0.75 倍。在源码里面使用的这个公式来算0.75(n - (n >>> 2)

    构造方法

    ConcurrentHashMap总共有5个构造方法,我们来看看。

    1. ConcurrentHashMap()
    2. ConcurrentHashMap(int initialCapacity)
    3. ConcurrentHashMap(int initialCapacity, float loadFactor)
    4. ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel)
    5. ConcurrentHashMap(Map<? extends K, ? extends V> m)

    其中的1是空方法。3最终调用的是4,传入参数concurrencyLevel值为1。而5对于我们来说使用频率比较低,这儿暂时不做分析。

    因此,我们真正需要分析的是2和4。

    1. ConcurrentHashMap(int initialCapacity)

    public ConcurrentHashMap(int initialCapacity) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException();
        int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
                   MAXIMUM_CAPACITY :
                   tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
        this.sizeCtl = cap;
    }
    

    该方法首先会对传入的初始容量大小做校验,不允许超过最大容量。其次会将传入的容量大小进行2^N转换。最后将转换后的容量大小设置到sizeCtl

    2. ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel)

    public ConcurrentHashMap(int initialCapacity,
                             float loadFactor, int concurrencyLevel) {
        // 负载因子和并发级别的校验。
        // 并发级别指的是预估的可能同时修改的线程数量,我们可以根据它来调整我们的初始容量。
        if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)
            throw new IllegalArgumentException();
        if (initialCapacity < concurrencyLevel)   // Use at least as many bins
            initialCapacity = concurrencyLevel;   // as estimated threads
        long size = (long)(1.0 + (long)initialCapacity / loadFactor);
        int cap = (size >= (long)MAXIMUM_CAPACITY) ?
            MAXIMUM_CAPACITY : tableSizeFor((int)size);
        this.sizeCtl = cap;
    }
    

    负载因子和并发级别的校验。并发级别指的是预估的可能同时修改的线程数量,我们可以根据它来调整我们的初始容量。默认的table.size为(1.0+初始容量)/负载因子

    总结

    至此我们对ConcurrentHashMap有一定的入门级了解,同时分析完了ConcurrentHashMap的构造方法。

    相关文章

      网友评论

          本文标题:ConcurrentHashMap源码分析(01)-构造方法

          本文链接:https://www.haomeiwen.com/subject/hdzrhqtx.html