ConcurrentHashMap(一):常量,成员变量,静态代码块,内部类,spread函数,tabAt函数等详解

散列表

Node节点(K-V)所放的位置:key的哈希值 & 数组.len-1

1.假如计算的下标为如图5,就会来到链表结构中,从头开始检查,看是否有key一致的节点,如果一致就进行替换,如果没有一致的节点就插入到末尾(要判断是否树化)

2.如果计算的下标为如图的10,可以看见是TreeBin节点(代表当前桶位已经树化成红黑树——可以提高查找的效率),其中TreeBin节点中维护了两个结构:红黑树结构和链表结构,先会插入到TreeNode链表结构,然后插入到TreeNode红黑树结构中(需要平衡操作)

3.如图的FWD节点 (扩容的时候可以用) ,处理完一个桶位之后,引用需要指向FWD节点,其他线程来到散列表时:1.如果是put操作,到FWD节点会帮你一起扩容。2.如果是get操作(FWD中保存了新表、扩容后表的引用)会到新表中执行查找操作。

4.扩容的顺序是从后往前迁移的——方便迭代的读,就是从前往后读的话,读到后面失去的数可以到新表去读(如果从低到高 容易冲突)

1_深入理解ConcurrentHashMap 常量

    /* ---------------- Constants -------------- */

    /**
     * The largest possible table capacity.  This value must be
     * exactly 1<<30 to stay within Java array allocation and indexing
     * bounds for power of two table sizes, and is further required
     * because the top two bits of 32bit hash fields are used for
     * control purposes.
     * 散列表数组最大限制
     */
    private static final int MAXIMUM_CAPACITY = 1 << 30;

    /**
     * The default initial table capacity.  Must be a power of 2
     * (i.e., at least 1) and at most MAXIMUM_CAPACITY.
     * 散列表默认值
     */
    private static final int DEFAULT_CAPACITY = 16;

    /**
     * The largest possible (non-power of two) array size.
     * Needed by toArray and related methods.
     */
    static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * The default concurrency level for this table. Unused but
     * defined for compatibility with previous versions of this class.
     * 并发级别,jdk1.7遗留下来的,1.8只有在初始化的时候用了一用。
     * 不代表并发级别。
     */
    private static final int DEFAULT_CONCURRENCY_LEVEL = 16;

    /**
     * The load factor for this table. Overrides of this value in
     * constructors affect only the initial table capacity.  The
     * actual floating point value isn't normally used -- it is
     * simpler to use expressions such as {@code n - (n >>> 2)} for
     * the associated resizing threshold.
     * 负载因子,JDK1.8中 ConcurrentHashMap 是固定值
     */
    private static final float LOAD_FACTOR = 0.75f;

    /**
     * The bin count threshold for using a tree rather than list for a
     * bin.  Bins are converted to trees when adding an element to a
     * bin with at least this many nodes. The value must be greater
     * than 2, and should be at least 8 to mesh with assumptions in
     * tree removal about conversion back to plain bins upon
     * shrinkage.
     * 树化阈值,指定桶位 链表长度达到8的话,有可能发生树化操作。
     */
    static final int TREEIFY_THRESHOLD = 8;

    /**
     * The bin count threshold for untreeifying a (split) bin during a
     * resize operation. Should be less than TREEIFY_THRESHOLD, and at
     * most 6 to mesh with shrinkage detection under removal.
     * 红黑树转化为链表的阈值
     */
    static final int UNTREEIFY_THRESHOLD = 6;

    /**
     * The smallest table capacity for which bins may be treeified.
     * (Otherwise the table is resized if too many nodes in a bin.)
     * The value should be at least 4 * TREEIFY_THRESHOLD to avoid
     * conflicts between resizing and treeification thresholds.
     * 联合TREEIFY_THRESHOLD控制桶位是否树化,只有当table数组长度达到64且 某个桶位 中的链表长度达到8,才会真正树化
     */
    static final int MIN_TREEIFY_CAPACITY = 64;

    /**
     * Minimum number of rebinnings per transfer step. Ranges are
     * subdivided to allow multiple resizer threads.  This value
     * serves as a lower bound to avoid resizers encountering
     * excessive memory contention.  The value should be at least
     * DEFAULT_CAPACITY.
     * 线程迁移数据最小步长,控制线程迁移任务最小区间一个值
     */
    private static final int MIN_TRANSFER_STRIDE = 16;

    /**
     * The number of bits used for generation stamp in sizeCtl.
     * Must be at least 6 for 32bit arrays.
     * 扩容相关,计算扩容时生成的一个标识戳(虽然没有用final修饰,但是全文没有修改他,不改变)
     * 不管什么线程来 16扩容到32都是不变的
     */
    private static int RESIZE_STAMP_BITS = 16;

    /**
     * The maximum number of threads that can help resize.
     * Must fit in 32 - RESIZE_STAMP_BITS bits.
     * 65535 表示并发扩容最多线程数
     */
    private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;

    /**
     * The bit shift for recording size stamp in sizeCtl.
     * 扩容相关
     */
    private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;

    /*
     * Encodings for Node hash fields. See above for explanation.
     */
    //node节点的hash为-1时,表示当前节点是FWD节点,已经迁移了
    static final int MOVED     = -1; // hash for forwarding nodes
    //node节点的hash为-2时,表示当前节点已经树化,
    // 表示当前节点为TreeBin节点,TreeBin节点代理操作红黑树
    static final int TREEBIN   = -2; // hash for roots of trees
    // ReservationNode的hash值
    static final int RESERVED  = -3; // hash for transient reservations
    //0x7fffffff =》0111 1111 1111 1111 1111 1111 1111 1111
    //可以将一个负数 位与运算后得到正数,但是不是绝对值
    static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash

    /** Number of CPUS, to place bounds on some sizings.
     * 当前系统的cpu数量
     */
    static final int NCPU = Runtime.getRuntime().availableProcessors();

    /** For serialization compatibility.
     * JDK1.8序列化 为了兼容jdk1.7的ConcurrentHashMap保存的
     */
    private static final ObjectStreamField[] serialPersistentFields = {
        new ObjectStreamField("segments", Segment[].class),
        new ObjectStreamField("segmentMask", Integer.TYPE),
        new ObjectStreamField("segmentShift", Integer.TYPE)
    };

2_深入理解ConcurrentHashMap 成员变量

    /* ---------------- Fields -------------- */

    /**
     * The array of bins. Lazily initialized upon first insertion.
     * Size is always a power of two. Accessed directly by iterators.
     * 散列表,长度一定是2次方数
     */
    transient volatile Node<K,V>[] table;

    /**
     * The next table to use; non-null only while resizing.
     * 扩容过程中,会将扩容中的新table 赋值给nextTable 保持引用,扩容结束之后,这里会被设置为Null
     */
    private transient volatile Node<K,V>[] nextTable;

    /**
     * Base counter value, used mainly when there is no contention,
     * but also as a fallback during table initialization
     * races. Updated via CAS.
     * 化整为零
     * LongAdder 中的 baseCount 未发生竞争时 或者 当前LongAdder处于加锁状态时,增量累到到baseCount中
     */
    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.
     * sizeCtl < 0
     * 1. -1 表示当前table正在初始化(有线程在创建table数组),当前线程需要自旋等待..
     * 2.表示当前table数组正在进行扩容 ,高16位表示:扩容的标识戳   低16位表示:(1 + nThread) 当前参与并发扩容的线程数量
     *
     * sizeCtl = 0,表示创建table数组时 使用DEFAULT_CAPACITY为大小
     *
     * sizeCtl > 0
     *
     * 1. 如果table未初始化,表示初始化大小
     * 2. 如果table已经初始化,表示下次扩容时的 触发条件(阈值)
     */
    private transient volatile int sizeCtl;

    /**
     * The next table index (plus one) to split while resizing.
     * 扩容过程中,记录当前进度。所有线程都需要从transferIndex中分配区间任务,去执行自己的任务。
     */
    private transient volatile int transferIndex;

    /**
     * Spinlock (locked via CAS) used when resizing and/or creating CounterCells.
     * LongAdder中的cellsBuzy 0表示当前LongAdder对象无锁状态,1表示当前LongAdder对象加锁状态)
     * 只有一个对象能持有加锁状态
     *
     */
    private transient volatile int cellsBusy;

    /**
     * Table of counter cells. When non-null, size is a power of 2.
     * LongAdder中的cells数组,当baseCount发生竞争后,会创建cells数组,
     * 线程会通过计算hash值 取到 自己的cell ,将增量累加到指定cell中
     * 总数 = sum(cells) + baseCount
     */
    private transient volatile CounterCell[] counterCells;

    // views
    private transient KeySetView<K,V> keySet;
    private transient ValuesView<K,V> values;
    private transient EntrySetView<K,V> entrySet;


3_分解静态代码块

    // Unsafe mechanics
    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属性在CounterCell中内存偏移地址*/
    private static final long CELLVALUE;
    /**表示数组第一个元素的偏移地址*/
    private static final long ABASE;
    private static final int ASHIFT;

    static {
        try {
            U = sun.misc.Unsafe.getUnsafe();
            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 表示Node[]数组中每一个单元所占用空间大小
            int scale = U.arrayIndexScale(ak);
            //1 0000 & 0 1111 = 0
            if ((scale & (scale - 1)) != 0)
                throw new Error("data type scale not a power of two");
            //numberOfLeadingZeros() 这个方法是返回当前数值转换为二进制后,从高位到低位开始统计,看有多少个0连续在一块。
            //8 => 1000 numberOfLeadingZeros(8) = 28
            //int是4个长度 100  32-3=29
            //4 => 100 numberOfLeadingZeros(4) = 29
            //ASHIFT = 31 - 29 = 2 ??  scale是局部变量
            //下标为5的数据
            //ABASE + (5 << ASHIFT)
            ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
        } catch (Exception e) {
            throw new Error(e);
        }
    }

4_分解内部类 Node、TreeNode、ForwardingNode

Node

    static class Node<K,V> implements Map.Entry<K,V> {
        //这个hash的K的hash经过一次扰动运算得出来的
        final int hash;
        final K key; //线程安全不能修改
        volatile V val;  //val可能修改,保持线程可见性
        volatile Node<K,V> 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)));
        }

        /**
         * Virtualized support for map.get(); overridden in subclasses.
         * 链表情况下用不到,当前桶位变成 treebin  fwd节点会用到
         */
        Node<K,V> find(int h, Object k) {
            Node<K,V> e = this;
            if (k != null) {
                do {
                    K ek;
                    if (e.hash == h &&
                        ((ek = e.key) == k || (ek != null && k.equals(ek))))
                        return e;
                } while ((e = e.next) != null);
            }
            return null;
        }
    }
 

TreeNode

    /**
     * Nodes for use in TreeBins
     */
    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;    // needed to unlink next upon deletion
        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;
        }
    }

ForWaidingNode

    /* ---------------- Special Nodes -------------- */

    /**
     * A node inserted at head of bins during transfer operations.
     */
    static final class ForwardingNode<K,V> extends Node<K,V> {
        //拿到fwd节点 代表现在再扩容数据正在迁移 写线程:需要参与并发扩容  读线程:调用find方法到新表继续查询
        final Node<K,V>[] nextTable;
        //hash固定
        ForwardingNode(Node<K,V>[] tab) {
            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
            //tab 一定不为空
            Node<K,V>[] tab = nextTable;
            outer: for (;;) {
                //n 表示为扩容而创建的 新表的长度
                //e 表示在扩容而创建新表使用 寻址算法 得到的 桶位头结点
                Node<K,V> e; int n;

                //条件一:永远不成立
                //条件二:永远不成立
                //条件三:永远不成立
                //条件四:在新扩容表中 重新定位 hash 对应的头结点
                //true -> 1.在oldTable中 对应的桶位在迁移之前就是null
                //        2.扩容完成后,有其它写线程,将此桶位设置为了null
                if (k == null || tab == null || (n = tab.length) == 0 ||
                    (e = tabAt(tab, (n - 1) & h)) == null)
                    return null;

                //前置条件:扩容后的表 对应hash的桶位一定不是null,e为此桶位的头结点
                //e可能为哪些node类型?
                //1.node 类型
                //2.TreeBin 类型
                //3.FWD 类型

                for (;;) {
                    //eh 新扩容后表指定桶位的当前节点的hash
                    //ek 新扩容后表指定桶位的当前节点的key
                    int eh; K ek;
                    //条件成立:说明新扩容 后的表,当前命中桶位中的数据,即为 查询想要数据。
                    if ((eh = e.hash) == h &&
                        ((ek = e.key) == k || (ek != null && k.equals(ek))))
                        return e;

                    //eh<0
                    //1.TreeBin 类型    2.FWD类型(新扩容的表,在并发很大的情况下,可能在此方法 再次拿到FWD类型..)
                    if (eh < 0) {
                        if (e instanceof ForwardingNode) {
                            tab = ((ForwardingNode<K,V>)e).nextTable;
                            continue outer;
                        }
                        else
                            //说明此桶位 为 TreeBin 节点,使用TreeBin.find 查找红黑树中相应节点。
                            return e.find(h, k);
                    }

                    //前置条件:当前桶位头结点 并没有命中查询,说明此桶位是 链表
                    //1.将当前元素 指向链表的下一个元素
                    //2.判断当前元素的下一个位置 是否为空
                    //   true->说明迭代到链表末尾,未找到对应的数据,返回Null
                    if ((e = e.next) == null)
                        return null;
                }
            }
        }
    }

5_小函数工具方法源码分解

5.1——spread(hash) 源码分析

    / 
	* 1100 0011 1010 0101 0001 1100 0001 1110
     * 0000 0000 0000 0000 1100 0011 1010 0101
     * 1100 0011 1010 0101 1101 1111 1011 1011
     * ---------------------------------------
     * 1100 0011 1010 0101 1101 1111 1011 1011
     * 0111 1111 1111 1111 1111 1111 1111 1111  ==HASH_BITS
     * 0100 0011 1010 0101 1101 1111 1011 1011
     *
     * 当前的hash右移16位 亦或 原来的hash  & HASH_BITS 得到正数的hash值
     * 让高16位 也参与到运算中来
     */
    static final int spread(int h) {
        return (h ^ (h >>> 16)) & HASH_BITS;
    }

5.2——tabAt(tab, index) 源码分析

    //获取数组指定下标位置的元素
    @SuppressWarnings("unchecked")
    static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
        return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
    }

5.3——casTabAt(tab, index, c, v )源码分析

    //用cas的方式去table的指定位置设置值,设置成功返回true
    static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
                                        Node<K,V> c, Node<K,V> v) {
        return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
    }

5.4——setTabAt(tab, index, v) 源码分析

    //指定位置设置值
    static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
        U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
    }

5.5——resizeStamp(int n) 源码分析

    /**
     * Returns the stamp bits for resizing a table of size n.
     * Must be negative when shifted left by RESIZE_STAMP_SHIFT.
     * 扩容标识戳 一致才能参与扩容
     * 16 -> 32
     * numberOfLeadingZeros(16) => 1 0000 =>27 =>0000 0000 0001 1011
     * |
     * (1 << (RESIZE_STAMP_BITS - 1)) => 1000 0000 0000 0000 => 32768
     * ---------------------------------------------------------------
     * 0000 0000 0001 1011
     * 1000 0000 0000 0000
     * 1000 0000 0001 1011
     */
    static final int resizeStamp(int n) {
        return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1));
    }

5.6——tableSizeFor(int c) 源码分析

    /**
     * Returns a power of two table size for the given desired capacity.
     * See Hackers Delight, sec 3.2
     * 返回>=c的最小的2的次方数
     * c=28
     * n=27 => 0b 11011
     * 11011 | 01101 => 11111
     * 11111 | 00111 => 11111
     * ....
     * => 11111 + 1 =100000 = 32
     */
    private static final int tableSizeFor(int c) {
        int n = c - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值