CurrentHashMap源码分析

1、ConcurrentHashmap的由来

        HashMap不是线程安全的,在多线程情况下会导致扩容出现循环链表,造成访问这个Entry的线程死循环,CPU接近100%;

        Hashtable,使用synchronized进行线程安全的控制,因为每次只有一个对象获取监视器锁,因此在高并发情况下,性能再次成为瓶颈。类似表锁;

        ConcurrentHashmap1.6版本,采用分段锁segment实现;segment继承ReentrantLock作为锁角色,提供安全保障;segment包含若干个桶,每个桶由HashEntry构成链表; 

        ConcurrentHashmap1.8版本优化:①不再采用分段锁,通过synchronized + CAS无锁操作保证安全性;②数据结构采用:数组+链表+红黑树; 数据结构上采用链表 +红黑树是为了保证稳定的查询效率;

            摈弃分段锁原因?    降低锁粒度,使得锁粒度不会随着扩容扩大并发度;

            采用synchronized而不使用重入锁的原因?   synchronized锁经过了偏向锁 轻量级锁  重量级锁的优化,性能得到提升; 因为synchronized是关键词,而可重入锁是实现类,在每个节点都通过重入锁来获取同步支持会带来巨大的内存消耗;

       总结: 无锁-表锁-分段锁-行锁

 

2-1、核心属性

  • table volatile Node<K,V>[] table:   //装载Node的数组,作为ConcurrentHashMap的数据容器,采用懒加载的方式,直到第一次插入数据的时候才会进行初始化操作,数组的大小总是为2的幂次方,默认初始大小为16。

  • nextTable volatile Node<K,V>[] nextTable; //扩容时使用,平时为null,只有在扩容的时候才为非null

  • sizeCtl volatile int sizeCtl; 该属性用来控制table数组的大小,根据是否初始化和是否正在扩容有几种情况: ①当值为负数时:1-1、如果为-1表示正在初始化;  1-2、如果为-N则表示当前正有N-1个线程进行扩容操作; ②当值为正数时:2-1、如果当前数组为null的话表示table在初始化过程中,sizeCtl表示为需要新建数组的长度; 2-2、若已经初始化了,表示当前数据容器(table数组)可用容量也可以理解成临界值(插入节点数超过了该临界值就需要扩容),具体指为数组的长度n 乘以 加载因子loadFactor; 2-3、当值为0时,即数组长度为默认初始值。

  • sun.misc.Unsafe U 在ConcurrentHashMapde的实现中可以看到大量的U.compareAndSwapXXXX的方法去修改ConcurrentHashMap的一些属性。这些方法实际上是利用了CAS算法保证了线程安全性,这是一种乐观策略,假设每一次操作都不会产生冲突,当且仅当冲突发生的时候再去尝试。而CAS操作依赖于现代处理器指令集,通过底层CMPXCHG指令实现。CAS(V,O,N)核心思想为:若当前变量实际值V与期望的旧值O相同,则表明该变量没被其他线程进行修改,因此可以安全的将新值N赋值给变量;若当前变量实际值V与期望的旧值O不相同,则表明该变量已经被其他线程做了处理,此时将新值N赋给变量操作就是不安全的,在进行重试。而在大量的同步组件和并发容器的实现中使用CAS是通过sun.misc.Unsafe类实现的,该类提供了一些可以直接操控内存和线程的底层操作;

2-2、关键内部类

  1. Node Node类实现了Map.Entry接口,主要存放key-value对,并且具有next域

     static class Node<K,V> implements Map.Entry<K,V> {
             final int hash;
             final K key;
             volatile V val;
             volatile Node<K,V> next;
     		......
     }

另外可以看出很多属性都是用volatile进行修饰的,也就是为了保证内存可见性。

  2.TreeNode 树节点,继承于承载数据的Node类。而红黑树的操作是针对TreeBin类的,从该类的注释也可以看出,也就是TreeBin会将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;
 		......
 }

   3.TreeBin 这个类并不负责包装用户的key、value信息,而是包装的很多TreeNode节点。实际的ConcurrentHashMap“数组”中,存放的是TreeBin对象,而不是TreeNode对象。

 static final class TreeBin<K,V> extends Node<K,V> {
         TreeNode<K,V> root;
         volatile TreeNode<K,V> first;
         volatile Thread waiter;
         volatile int lockState;
         // values for lockState
         static final int WRITER = 1; // set while holding write lock
         static final int WAITER = 2; // set when waiting for write lock
         static final int READER = 4; // increment value for setting read lock
 		......
 }

  4.ForwardingNode 在扩容时才会出现的特殊节点,其key,value,hash全部为null。并拥有nextTable指针引用新的table数组。

 static final class ForwardingNode<K,V> extends Node<K,V> {
     final Node<K,V>[] nextTable;
     ForwardingNode(Node<K,V>[] tab) {
         super(MOVED, null, null, null);
         this.nextTable = tab;
     }
    .....
 }

 

2-3、CAS操作

    1、tabAt(Node<K,V>[] tab, int i)   获取table数组中索引为i的Node元素

    2、casTabAt(Node<K,V>[] tab, int i, Node<K,V> c, Node<K,V> v)利用CAS操作设置table数组中索引为i的元素

    3、setTabAt(Node<K,V>[] tab, int i, Node<K,V> v)设置table数组中索引为i的元素

 

 

3-1、源码分析:put

     final V putVal(K key, V value, boolean onlyIfAbsent) {
        if (key == null || value == null) throw new NullPointerException();
        int hash = spread(key.hashCode());   //key取hash值;
        int binCount = 0;
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
            if (tab == null || (n = tab.length) == 0)
                tab = initTable();      //@1  第一次put:table不存在则初始化
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {  //(n - 1) & hash利用N为2的n次方进行巧妙的与取模;
                if (casTabAt(tab, i, null,
                             new Node<K,V>(hash, key, value, null)))
                    break;   // 第一次put新构造的Node节点到桶,直接put,成功后直接跳出循环;
            }
            else if ((fh = f.hash) == MOVED)  //@2 当前Entry的hash值为MOVED立即帮助扩容;当并发越高的时候也可以实现更快速的扩容;充分利用并发性;
                tab = helpTransfer(tab, f);
            else {
                V oldVal = null;
                synchronized (f) {   //锁住当前的Entry
                    if (tabAt(tab, i) == f) {
                        if (fh >= 0) {    //当前为链表,在链表中插入新的键值对
                            binCount = 1;   //链表长度
                            for (Node<K,V> e = f;; ++binCount) {
                                K ek;
                                if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {  //当元素key值相同则覆盖值
                                    oldVal = e.val;
                                    if (!onlyIfAbsent)
                                        e.val = value;
                                    break;
                                }
                                Node<K,V> pred = e;
                                if ((e = e.next) == null) {  //查找链表的末尾节点,将新元素赋值给这个节点;
                                    pred.next = new Node<K,V>(hash, key,
                                                              value, null);
                                    break;
                                }
                            }
                        }
                        else if (f instanceof TreeBin) {   //当前为红黑树,在红黑树中插入新的键值对
                            Node<K,V> p;
                            binCount = 2;
                            if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                           value)) != null) {
                                oldVal = p.val;
                                if (!onlyIfAbsent)
                                    p.val = value;
                            }
                        }
                    }
                }
                if (binCount != 0) {
                    if (binCount >= TREEIFY_THRESHOLD)
                        treeifyBin(tab, i);   //@ 3当实际大小超过TREEIFY_THRESHOLD-8 转换为红黑树
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
        addCount(1L, binCount);   // @4 put成功则计算器增加1;这里有优秀的设计模式;判断是否需要扩容
        return null;
    }




//@1  第一次put:table不存在则初始化   initTable(); 
     private final Node<K,V>[] initTable() {
        Node<K,V>[] tab; int sc;
        while ((tab = table) == null || tab.length == 0) {
            if ((sc = sizeCtl) < 0)  //sizeCtl < 0 保证只有一个线程正在进行初始化; sizeCtl = 0 数组长度为默认初始值;  sizeCtl > 0 为数组长度
                Thread.yield(); // lost initialization race; just spin
            else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {  //开始初始化设置 SIZECTL = -1
                try {
                    if ((tab = table) == null || tab.length == 0) {
                        int n = (sc > 0) ? sc : DEFAULT_CAPACITY;  //得出数组大小
                        @SuppressWarnings("unchecked")
                        Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
                        table = tab = nt;
                        sc = n - (n >>> 2);  //设置sizeCtl 为扩容临界值
                    }
                } finally {
                    sizeCtl = sc;
                }
                break;
            }
        }
        return tab;
    }



//@2 当前Entry的hash值为MOVED立即帮助扩容   tab = helpTransfer(tab, f);
    final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
        Node<K,V>[] nextTab; int sc;
        if (tab != null && (f instanceof ForwardingNode) &&
            (nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {  //nextTable指向新的引用数组
            int rs = resizeStamp(tab.length);  //加工标记位,与扩容前的数组长度相关
            while (nextTab == nextTable && table == tab &&
                   (sc = sizeCtl) < 0) {
                  //sizeCtl 1000 0000 0001 1011 0000 0000 0000 0010 高16位为标记位,低16位为扩容线程数;设计目的:确保并发扩容,扩容戳唯一;
                if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||  //扩容标记相等 || 预定线程数为1  || 线程数已经达到最大容量 || 扩容下标为0-扩容已完成   
                    sc == rs + MAX_RESIZERS || transferIndex <= 0)  //则不进行扩容
                    break;
                if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
                    transfer(tab, nextTab);   // @5 重点:扩容逻辑;
                    break;
                }
            }
            return nextTab;
        }
        return table;
    }


    //1左移15位, | 无符号整型的最高非零位前面0的个数 ;  
    //16 = 0000 0000 0000 0000 0000 0000 0001 0000; 
    // Integer.numberOfLeadingZeros(16) = 27   = 0001 1011
    // 1 << (RESIZE_STAMP_BITS - 1) = 0000 0000 0000 0000 1000 0000 0000 0000
    // 结果:0000 0000 0000 0000 1000 0000 0001 1011  
    static final int resizeStamp(int n) {
        return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1));
    }






//@ 3当实际大小超过TREEIFY_THRESHOLD 转换为红黑树   treeifyBin(tab, i);   
//  红黑树简单介绍:  红黑树4条原则;   本身是一个平衡二叉树,确保到任何一个叶节点查询效率一致 ;    平衡方法:通过左旋、右旋实现;





// @4 put成功则计算器增加1;这里有优秀的设计模式;判断是否需要扩容 addCount(1L, binCount);   
    private final void addCount(long x, int check) {
        CounterCell[] as; long b, s;
        if ((as = counterCells) != null ||
            !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) { //对 baseCount做原子递增,仅尝试一次,失败则放弃;
            /**
             *在高并发下如何高效安全的实现存储数据个数的统计?
             *设计思想:分而治之;  加锁会导致性能下降,case在高并发情况下会循环cas造成性能下降;
             *    用 CounterCell[] 去分别记录元素的个数,分片处理,降低并发;并发加大时也可以增加数组长度;
             *   sumCount() 汇总元素个数;baseCount:元素基本个数;
             */
            CounterCell a; long v; int m;
            boolean uncontended = true;
            if (as == null || (m = as.length - 1) < 0 ||
                (a = as[ThreadLocalRandom.getProbe() & m]) == null ||   // 生成一个线程安全的随机数(线程安全的随机数);
                !(uncontended =
                  U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {  
                fullAddCount(x, uncontended); //增加计数:首先初始化当前的CounterCell[],然后当当前节点为null则初始化节点CounterCell;然后存在CounterCell 时则CAS累加;
                return;
            }
            if (check <= 1)
                return;
            s = sumCount();
        }
        if (check >= 0) {
            Node<K,V>[] tab, nt; int n, sc;
            while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
                   (n = tab.length) < MAXIMUM_CAPACITY) {  //扩容:大于扩容因子 && tab存在 && tab长度 < 最大容量
                int rs = resizeStamp(n);
                if (sc < 0) {
                    if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
                        sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
                        transferIndex <= 0)
                        break;
                    if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) 
                        transfer(tab, nt);
                }
                else if (U.compareAndSwapInt(this, SIZECTL, sc,
                                             (rs << RESIZE_STAMP_SHIFT) + 2))
                    transfer(tab, null);
                s = sumCount();
            }
        }
    }






// @5 重点:扩容逻辑:  1,扩大数组长度    2,转移原本的数据链-进行数据迁移
 private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
        int n = tab.length, stride;
        if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
            stride = MIN_TRANSFER_STRIDE; // 细分:每个cpu至少处理16个Entry,的扩容迁移,cpu为1则全部由该cpu处理;
        if (nextTab == null) {            // initiating
            try {
                @SuppressWarnings("unchecked")
                Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];  //2倍扩容
                nextTab = nt;
            } catch (Throwable ex) {      // try to cope with OOME
                sizeCtl = Integer.MAX_VALUE;
                return;
            }
            nextTable = nextTab;
            transferIndex = n;
        }
        int nextn = nextTab.length;
        ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab); //nextTab为新table
        boolean advance = true;
        boolean finishing = false; // to ensure sweep before committing nextTab
        for (int i = 0, bound = 0;;) {
            Node<K,V> f; int fh;
            while (advance) {    //分配节点槽位
                int nextIndex, nextBound;
                if (--i >= bound || finishing)
                    advance = false;
                else if ((nextIndex = transferIndex) <= 0) {
                    i = -1;
                    advance = false;
                }
                else if (U.compareAndSwapInt   //第一个进程进来:i=31 bound = 16是其处理范围的槽点;第二个线程进来为 i= 15  bound = 0;
                         (this, TRANSFERINDEX, nextIndex,
                          nextBound = (nextIndex > stride ?
                                       nextIndex - stride : 0))) {
                    bound = nextBound;
                    i = nextIndex - 1;
                    advance = false;
                }
            }
            if (i < 0 || i >= n || i + n >= nextn) {  //当前线程如果处理完成则对扩容线程数-1,释放调用线程;
                int sc;
                if (finishing) {
                    nextTable = null;
                    table = nextTab;
                    sizeCtl = (n << 1) - (n >>> 1);
                    return;
                }
                if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
                    if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)
                        return;
                    finishing = advance = true;
                    i = n; // recheck before commit
                }
            }
            else if ((f = tabAt(tab, i)) == null)
                advance = casTabAt(tab, i, null, fwd);  //已经处理过了,是一个null节点
            else if ((fh = f.hash) == MOVED)
                advance = true; // already processed   进入下一次循环
            else {    //开始数据迁移,确认高低链;
                synchronized (f) {
                    if (tabAt(tab, i) == f) {
                        Node<K,V> ln, hn;
                        if (fh >= 0) {   //当为链表的时候
                            int runBit = fh & n;   //当前节点的hash值 & n(数组长度),得到当前链表是高位1  还是低位0
                            Node<K,V> lastRun = f;
                            for (Node<K,V> p = f.next; p != null; p = p.next) {
                                int b = p.hash & n;
                                if (b != runBit) {
                                    runBit = b;
                                    lastRun = p;
                                }
                            }
                            if (runBit == 0) {
                                ln = lastRun;
                                hn = null;
                            }
                            else {
                                hn = lastRun;
                                ln = null;
                            }
                            //组装高位链和低位链
                            for (Node<K,V> p = f; p != lastRun; p = p.next) {
                                int ph = p.hash; K pk = p.key; V pv = p.val;
                                if ((ph & n) == 0)
                                    ln = new Node<K,V>(ph, pk, pv, ln);
                                else
                                    hn = new Node<K,V>(ph, pk, pv, hn);
                            }
                            setTabAt(nextTab, i, ln);   //低位链保持偏移量不变;
                            setTabAt(nextTab, i + n, hn);  高位链迁移到 + n 的位置
                            setTabAt(tab, i, fwd);
                            advance = true;
                        }
                        else if (f instanceof TreeBin) {  //当为红黑树的时候
                            TreeBin<K,V> t = (TreeBin<K,V>)f;
                            TreeNode<K,V> lo = null, loTail = null;
                            TreeNode<K,V> hi = null, hiTail = null;
                            int lc = 0, hc = 0;
                            for (Node<K,V> e = t.first; e != null; e = e.next) {
                                int h = e.hash;
                                TreeNode<K,V> p = new TreeNode<K,V>
                                    (h, e.key, e.val, null, null);
                                if ((h & n) == 0) {
                                    if ((p.prev = loTail) == null)
                                        lo = p;
                                    else
                                        loTail.next = p;
                                    loTail = p;
                                    ++lc;
                                }
                                else {
                                    if ((p.prev = hiTail) == null)
                                        hi = p;
                                    else
                                        hiTail.next = p;
                                    hiTail = p;
                                    ++hc;
                                }
                            }
                            ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :
                                (hc != 0) ? new TreeBin<K,V>(lo) : t;
                            hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :
                                (lc != 0) ? new TreeBin<K,V>(hi) : t;
                            setTabAt(nextTab, i, ln);
                            setTabAt(nextTab, i + n, hn);
                            setTabAt(tab, i, fwd);
                            advance = true;
                        }
                    }
                }
            }
        }
    }

 

3-2、源码分析:get

public V get(Object key) {
    Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
	// 1. 重hash
    int h = spread(key.hashCode());
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (e = tabAt(tab, (n - 1) & h)) != null) {
        // 2. table[i]桶节点的key与查找的key相同,则直接返回
		if ((eh = e.hash) == h) {
            if ((ek = e.key) == key || (ek != null && key.equals(ek)))
                return e.val;
        }
		// 3. 当前节点hash小于0说明为树节点,在红黑树中查找即可
        else if (eh < 0)
            return (p = e.find(h, key)) != null ? p.val : null;
        while ((e = e.next) != null) {
		//4. 从链表中查找,查找到则返回该节点的value,否则就返回null即可
            if (e.hash == h &&
                ((ek = e.key) == key || (ek != null && key.equals(ek))))
                return e.val;
        }
    }
    return null;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值