ConcurrentHashMap源码解析1.内部结构

参考小刘源码

文章参考路飞大佬

简介

  • ConcurrentHashMap是HashMap的线程安全版本,内部也是使用(数组+链表+红黑树) 的结构来存储元素,相比于同样线程安全的HashTable(底层方法都加了synchronized)来说,效率各方面都有极大的提升

​ ConcurrentHashMap的整体流程

整体流程跟HashMap比较类似,大致是以下几步。

  • 1、如果桶数组未初始化,则初始化
  • 2、如果待插入的元素所在的桶为空,则尝试把元素直接插入到桶的第一个位置
  • 3、如果正在扩容,则当前线程一起加入到扩容的过程中
  • 4、如果待插入元素所在的桶不为空且不在迁移元素,则锁住这个桶(分段锁)
  • 5、如果当前桶中元素以链表方式存储,则在链表中寻找该元素或者插入元素
  • 6、如果当前桶中元素以红黑树方式存储,则在红黑树中寻找该元素或者插入元素
  • 7、如果元素存在,则返回旧值
  • 8、如果元素不存在,整个Map的元素个数+1,并检查是否需要扩容。

添加元素操作中使用的锁主要有(自旋锁(CAS) + synchronized+分段锁)

为什么是synchronized不是ReentrantLock?

因为synchronized已经得到了极大地优化,在特定情况下并不比ReentrantLock差

JDK1.8ConcurrentHashMap结构图

在这里插入图片描述

1.1常量

public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
    implements ConcurrentMap<K,V>, Serializable {
    
    
    
    private static final long serialVersionUID = 7249069246763182397L;
  
    //数组最大长度 
    private static final int MAXIMUM_CAPACITY = 1 << 30;

	//哈希表默认长度
    private static final int DEFAULT_CAPACITY = 16;


	//默认并发级别 jdk1.7遗留下来的,1.8只是初始化的时候用了,1.8不代表并发级别
    private static final int DEFAULT_CONCURRENCY_LEVEL = 16;

	//默认加载因子
    private static final float LOAD_FACTOR = 0.75f;

	//树化阈值 和MIN_TREEIFY_CAPACITY一起控制(链表长度大于8并且数组长度大于64才转为红黑树)
    static final int TREEIFY_THRESHOLD = 8;
	
    //树转链表阈值 
    static final int UNTREEIFY_THRESHOLD = 6;

   	//树化条件二 数组长度达到64
    static final int MIN_TREEIFY_CAPACITY = 64;

    //线程迁移数据最小步长,控制线程迁移任务最小区间的一个值(即桶位的跨度)
    private static final int MIN_TRANSFER_STRIDE = 16;

    //扩容相关,计算扩容时生成的一个标识戳
    private static int RESIZE_STAMP_BITS = 16;

    // 计算出来的值是65535((1 << 16) - 1) 代表并发扩容的最多线程数
    private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;

    //扩容相关, 
    private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;

    //当node节点的hash值是-1时,表示当前节点时FWD节点(已经被迁移的节点)
    static final int MOVED     = -1; 
    
    //node节点的hash值是-2,表示当前节点已经树化,且当前节点为TreeBin对象,TreeBin对象代	   理操作红黑树
    static final int TREEBIN   = -2; 
  
    static final int RESERVED  = -3; 
    
    //0x7fffffff -> (0111 后面全是1) 可以将一个负数通过&运算后得到正数,但不是取绝对值
    static final int HASH_BITS = 0x7fffffff; 

    //当前系统的CPU数量 
    static final int NCPU = Runtime.getRuntime().availableProcessors();

	//1.8序列化 为了兼容1.7的ConcurrentHashMap保存的(核心代码没有用到)
    private static final ObjectStreamField[] serialPersistentFields = {
    
    
        new ObjectStreamField("segments", Segment[].class),
        new ObjectStreamField("segmentMask", Integer.TYPE),
        new ObjectStream Field("segmentShift", Integer.TYPE)
    };

1.2成员属性

    //散列表 长度一定是2的次方数
    transient volatile Node<K,V>[] table;

    //扩容过程中,会将扩容中的新table赋值给nextTable保持引用,扩容结束后会被设置为NULL
    private transient volatile Node<K,V>[] nextTable;
 
    //LongAdder中的baseCount,未发生竞争时或者Cell数组初始化被加锁时 将数据写入到base中
    private transient volatile long baseCount;

    /*
     * 1、sizeCtl < 0 
     *     1.1 sizeCtl = -1 表示有线程正在创建table数组,当前线程需要自旋等待
     *     1.2 其他表示当前table数组正在扩容,高16位表示扩容的时间戳,低16位表示 		      *	    (1 + nThread)当前参数并发扩容的线程数量
     * 
     *  2、sizeCtl = 0
     *    表示创建table数组时,使用DEFAULT_CAPACITY(16)大小
     *   
     *  3、sizeCtl > 0 
     *     两种情况   
     *     1.如果table未初始化,表示初始化大小
     *     2.如果table已经初始化,表示下次扩容时的触发条件(阈值)
     */ 
    private transient volatile int sizeCtl;

  //扩容过程中 记录当前进度,所有线程都需要从transferIndex中分配区间任务,去执行自己的任务
    private transient volatile int transferIndex;

    //LongAdder中的cellsBusy 相当于锁(同步状态 0代表无锁 1代表有锁)
    private transient volatile int cellsBusy;

    //LongAdder中的Clles数组 当baseCount发生竞争后,会创建Cells数组
    //线程会通过计算hash值 去到对应的cell中,将值写到cell中。
    //最后的求和就是base + Cells数组中的所有值。
    private transient volatile CounterCell[] counterCells;

    // 相当于HashMap中的keySet、values和entrySet。
    private transient KeySetView<K,V> keySet;
    private transient ValuesView<K,V> values;
    private transient EntrySetView<K,V> entrySet;

1.3静态代码块

    //UnSafe类 底层都是native方法,调用C++的方法,直接操作内存。
    private static final sun.misc.Unsafe U;
    //表示sizeCtl属性在ConcurrentHashMap中内存偏移地址
    private static final long SIZECTL;
    //表示TRANSFERINDEX属性在ConcurrentHashMap中内存偏移地址
    private static final long TRANSFERINDEX;
    //表示BASECOUNT属性在ConcurrentHashMap中内存偏移地址
    private static final long BASECOUNT;
    //表示CELLSBUSY属性在ConcurrentHashMap中内存偏移地址
    private static final long CELLSBUSY;
    //表示CELLVALUE属性在ConcurrentHashMap中内存偏移地址
    private static final long CELLVALUE;
    //表示table数组中第一个元素的偏移地址
    private static final long ABASE;
    private static final int ASHIFT;

    static {
    
    
        try {
    
    
            U = sun.misc.Unsafe.getUnsafe(); //获取UnSafe对象
            Class<?> k = ConcurrentHashMap.class;
            //获取地址的偏移量,直接操作内存
            SIZECTL = U.objectFieldOffset
                (k.getDeclaredField("sizeCtl"));
            TRANSFERINDEX = U.objectFieldOffset
                (k.getDeclaredField("transferIndex"));
            BASECOUNT = U.objectFieldOffset
                (k.getDeclaredField("baseCount"));
            CELLSBUSY = U.objectFieldOffset
                (k.getDeclaredField("cellsBusy"));
            Class<?> ck = CounterCell.class;
            CELLVALUE = U.objectFieldOffset
                (ck.getDeclaredField("value"));
            Class<?> ak = Node[].class;
            //拿到数组的第一个元素的偏移地址
            ABASE = U.arrayBaseOffset(ak); 
            //拿到数组单元占用空间大小,scale表示table数组中每一个单元所占用空间大小。
            //结合上面的ABASE, 就可以访问数组中的每一个位置的元素
            int scale = U.arrayIndexScale(ak);
            //判断scale是否是2的幂
            if ((scale & (scale - 1)) != 0)
                throw new Error("data type scale not a power of two");
            //为了内存寻址(找数组位置)用。
            ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
        } catch (Exception e) {
    
    
            throw new Error(e);
        }
    }
}

1.4内部类

1.4.1Node类

    static class Node<K,V> implements Map.Entry<K,V> {
    
    
        final int hash; //key的哈希值
        final K key;   
        volatile V val; // volatile修饰 保证内存可见行
        volatile Node<K,V> next; //next指针

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

        public final K getKey()       {
    
     return key; }
        public final V getValue()     {
    
     return val; }
        public final int hashCode()   {
    
     return key.hashCode() ^ val.hashCode(); }
        public final String toString(){
    
     return key + "=" + val; }
        public final V setValue(V value) {
    
    
            throw new UnsupportedOperationException();
        }
		
        //比较两个节点是否完全相同 
        public final boolean equals(Object o) {
    
    
            Object k, v, u; Map.Entry<?,?> e;
            return ((o instanceof Map.Entry) &&
                    (k = (e = (Map.Entry<?,?>)o).getKey()) != null &&
                    (v = e.getValue()) != null &&
                    (k == key || k.equals(key)) &&
                    (v == (u = val) || v.equals(u)));
        }

        
        Node<K,V> find(int h, Object k) {
    
    
            Node<K,V> e = this;
            if (k != null) {
    
    
                do {
    
    
                    K ek;
                    //判断两个节点的key是否相同,先比较哈希值,然后比较内存地址和equals
                    if (e.hash == h &&
                        ((ek = e.key) == k || (ek != null && k.equals(ek))))
                        return e;
                } while ((e = e.next) != null);
            }
            return null;
        }
    }

1.4.2ForwardingNode

 //继承Node节点
static final class ForwardingNode<K,V> extends Node<K,V> {
    
    
        /* 表示新哈希表的引用 表示正在扩容或者数据正在迁移的过程。
         *  1.如果当前是写线程,帮助进行并发扩容
         *  2.如果是读线程,调用find方法区新哈希表中去查询。
         */
        final Node<K,V>[] nextTable;
        ForwardingNode(Node<K,V>[] tab) {
    
    
            //hash值默认是-1 其他属性都是null。
            super(MOVED, null, null, null);
            this.nextTable = tab;
        }
		
    	//到新表中取读数据
        Node<K,V> find(int h, Object k) {
    
    
            // loop to avoid arbitrarily deep recursion on forwarding nodes
            outer: for (Node<K,V>[] tab = nextTable;;) {
    
    
                Node<K,V> e; int n;
                if (k == null || tab == null || (n = tab.length) == 0 ||
                    (e = tabAt(tab, (n - 1) & h)) == null)
                    return null;
                for (;;) {
    
    
                    int eh; K ek;
                    if ((eh = e.hash) == h &&
                        ((ek = e.key) == k || (ek != null && k.equals(ek))))
                        return e;
                    if (eh < 0) {
    
    
                        if (e instanceof ForwardingNode) {
    
    
                            tab = ((ForwardingNode<K,V>)e).nextTable;
                            continue outer;
                        }
                        else
                            return e.find(h, k);
                    }
                    if ((e = e.next) == null)
                        return null;
                }
            }
        }
    }

1.4.3TreeNode

 static final class TreeNode<K,V> extends Node<K,V> {
    
    
        //父节点
        TreeNode<K,V> parent;  // red-black tree links
        TreeNode<K,V> left;    //左子节点
        TreeNode<K,V> right;   //右子节点
        TreeNode<K,V> prev;    //前驱节点
        boolean red; //节点的两种颜色 红与黑

        TreeNode(int hash, K key, V val, Node<K,V> next,
                 TreeNode<K,V> parent) {
    
    
            super(hash, key, val, next);
            this.parent = parent;
        }

        Node<K,V> find(int h, Object k) {
    
    
            return findTreeNode(h, k, null);
        }

        /**
         * Returns the TreeNode (or null if not found) for the given key
         * starting at given root.
         */
        final TreeNode<K,V> findTreeNode(int h, Object k, Class<?> kc) {
    
    
            if (k != null) {
    
    
                TreeNode<K,V> p = this;
                do  {
    
    
                    int ph, dir; K pk; TreeNode<K,V> q;
                    TreeNode<K,V> pl = p.left, pr = p.right;
                    if ((ph = p.hash) > h)
                        p = pl;
                    else if (ph < h)
                        p = pr;
                    else if ((pk = p.key) == k || (pk != null && k.equals(pk)))
                        return p;
                    else if (pl == null)
                        p = pr;
                    else if (pr == null)
                        p = pl;
                    else if ((kc != null ||
                              (kc = comparableClassFor(k)) != null) &&
                             (dir = compareComparables(kc, k, pk)) != 0)
                        p = (dir < 0) ? pl : pr;
                    else if ((q = pr.findTreeNode(h, k, kc)) != null)
                        return q;
                    else
                        p = pl;
                } while (p != null);
            }
            return null;
        }
    }

1.5构造方法

//无参构造方法,没有初始化table,懒加载
public ConcurrentHashMap() {
    
    
}


//--------------------------一个参数构造器--------------------------------------

//传入一个初始容量 最终就是为sizeCtl赋值 
public ConcurrentHashMap(int initialCapacity) {
    
    
    //校验
    if (initialCapacity < 0)
        throw new IllegalArgumentException();
    
    /*
     * 当传入的初始容量大于规定的最大容量(2的30次方的一半时),直接赋一个最大长度
	 * 否则进入tableForSize方法,跟HashMap中的方法一直,传入一个数值,返回一个大于这个数	  *	的最小的2的幂 例如传入24 返回一个32.
	 *
	 *   最终将这个2的幂赋值给sizeCtl
     */
    int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
            MAXIMUM_CAPACITY :
            tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
    this.sizeCtl = cap;
}

//----------------------传入一个Map的构造器-------------------------------------

public ConcurrentHashMap(Map<? extends K, ? extends V> m) {
    
    
    this.sizeCtl = DEFAULT_CAPACITY;
    putAll(m);
}

//-----------------两个参数构造器-----------------------------------------------

public ConcurrentHashMap(int initialCapacity, float loadFactor) {
    
    
    this(initialCapacity, loadFactor, 1);
}

	/*默认调用的是这个三个参数的构造器
	 *  
 	 *	传入的loadFactor和concurrencyLevel最终目的就是为了最终计算出来一个2的幂赋值给
 	 *  sizeCtl。 
	 */
public ConcurrentHashMap(int initialCapacity,
                         float loadFactor, int concurrencyLevel) {
    
    
    if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)
        throw new IllegalArgumentException();
    if (initialCapacity < concurrencyLevel)  
        initialCapacity = concurrencyLevel;   
    long size = (long)(1.0 + (long)initialCapacity / loadFactor);
    int cap = (size >= (long)MAXIMUM_CAPACITY) ?
            MAXIMUM_CAPACITY : tableSizeFor((int)size);
    this.sizeCtl = cap; 
}

从这里可以看出,这些构造器的最终目的就是为sizeCtl属性赋值(空参表示sizeCtl为0,构造table时长度为默认值16),前面我们也解释了sizeCtl的值对应的情况。

/*
     * 1、sizeCtl < 0 
     *     1.1 sizeCtl = -1 表示有线程正在创建table数组,当前线程需要自旋等待
     *     1.2 其他表示当前table数组正在扩容,高16位表示扩容的时间戳,低16位表示 (1 + nThread)当前参数并发扩容的线程数量
     * 
     *  2、sizeCtl = 0
     *    表示创建table数组时,使用DEFAULT_CAPACITY(16)大小
     *   
     *  3、sizeCtl > 0 
     *     两种情况   
     *     1.如果table未初始化,表示初始化大小
     *     2.如果table已经初始化,表示下次扩容时的触发条件(阈值)
     */ 

猜你喜欢

转载自blog.csdn.net/qq_46312987/article/details/121513068
今日推荐