zoukankan      html  css  js  c++  java
  • 源码解析之ConcurrentHashmap

    ConcurrentHashmap算是我看的集合源码里最难理解的了(当然ConcurrentLinkedList虽然代码少但理解起来也累),在Java1.8版本中DougLea大师巧通过妙地代码把锁粒度已经将成桶级别了,不得不说非常厉害。本文暂时贴上代码,内容后续补充。

    看ConcurrentHashmap之前要掌握的基础。

    1、对Hashmap的原理了解。

    2、Volatile关键字、CAS操作和Synchronized关键字要理解。

    3、配合网上解析和并发的书一同食用,而且要看源码里的注释,看源码前先了解其运作过程。

    推荐一篇源码解析:https://www.jianshu.com/p/487d00afe6ca

    推荐的书《Java并发编程的艺术》 集合那章

    正文

    get操作

        public V get(Object key) {
            Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
            int h = spread(key.hashCode());  //高16位与低6位散列
            if ((tab = table) != null && (n = tab.length) > 0 &&
                    (e = tabAt(tab, (n - 1) & h)) != null) {
                if ((eh = e.hash) == h) {
                    if ((ek = e.key) == key || (ek != null && key.equals(ek)))
                        return e.val;
                }
                else if (eh < 0)  //eh小于0表示该table正在扩容,将旧table上的node移到新table上,被移过去的节点旧位置上标记一个hash<0的node
                       //find就是用this找到本该在这里的节点,然后判断是否为null返回相应值。
    return (p = e.find(h, key)) != null ? p.val : null; while ((e = e.next) != null) { if (e.hash == h && ((ek = e.key) == key || (ek != null && key.equals(ek)))) return e.val; } } return null; }

    put操作

    public V put(K key, V value) {
            return putVal(key, value, false);
        }
    final V putVal(K key, V value, boolean onlyIfAbsent) {
            if (key == null || value == null) throw new Nul lPointerException();
            int hash = spread(key.hashCode()); //(h ^ (h >>> 16)) & HASH_BITS
            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();  //初始化Node数组table
                else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {//数组不为空,分配到的地址中没有node
                    if (casTabAt(tab, i, null,
                            new Node<K,V>(hash, key, value, null)))//cas操作把它加放入该地址
                        break;                   // no lock when adding to empty bin  数组每个位置上的第一个节点不需要获得锁
                }
                else if ((fh = f.hash) == MOVED)//?? 好像是扩容时被put的操作
                    tab = helpTransfer(tab, f);
                else {//数组被初始化了且地址不为空,非扩容时期的正常操作
                    V oldVal = null;
                    synchronized (f) {//拿到该位置第一个节点的对象锁
                        if (tabAt(tab, i) == f) {//再次确认头节点
                            if (fh >= 0) {//头节点hash>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) { //f是tree节点,用红黑树方法
                                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);
                        if (oldVal != null)//如果是更新值,返回oldval
                            return oldVal;
                        break;
                    }
                }
            }
            addCount(1L, binCount);
            return null;
        }
    private final Node<K,V>[] initTable() {
            Node<K,V>[] tab; int sc;
            while ((tab = table) == null || tab.length == 0) {
                if ((sc = sizeCtl) < 0) //已被别人抢先初始化了(第一个初始化的线程将sizeCtl改为-1) 进入准备状态(等待被唤醒
                    Thread.yield(); // lost initialization race; just spin
                else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
                    try {
                        if ((tab = table) == null || tab.length == 0) {
                            int n = (sc > 0) ? sc : DEFAULT_CAPACITY; //sizeCtl大于0就使用它的大小,等于0默认容量大小
                            @SuppressWarnings("unchecked")
                            Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];//new个node数组
                            table = tab = nt;
                            sc = n - (n >>> 2);//sc=0.75n
                        }
                    } finally {
                        sizeCtl = sc;//sizeCtl为0.75n  sizeCtl像是数组扩容阈值
                    }
                    break;
                }
            }
            return tab;
        }
    final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) { //与后面addcount()函数代码相似,这里就不解释了,直接往后看。   
            Node<K,V>[] nextTab; int sc;                //函数大意就是 满足一定条件也进入transfer方法 帮助扩容。
            if (tab != null && (f instanceof ForwardingNode) &&  //
                    (nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
                int rs = resizeStamp(tab.length);
                while (nextTab == nextTable && table == tab &&
                        (sc = sizeCtl) < 0) {
                    if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
                            sc == rs + MAX_RESIZERS || transferIndex <= 0)
                        break;
                    if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
                        transfer(tab, nextTab);
                        break;
                    }
                }
                return nextTab;
            }
            return table;
        }
    private final void addCount(long x, int check) {
            CounterCell[] as; long b, s;
            if ((as = counterCells) != null ||   //countercells为2e幂,应该相当于数组长度  basecount应该是实时键值对数量
                    !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
                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);
                    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) { //键值对数量超过了阈值,且小于最大值
                    int rs = resizeStamp(n); //Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1));
                    if (sc < 0) {//其他线程正在扩容
                        //第一个条件:因为第一个线程扩容后会将sc设为rs << RESIZE_STAMP_SHIFT) + 2),它退回去会等于rs,如果
                        //不等于说明第一个线程还没开始扩容。
                        //第二、三个条件:未知
                        //第四个条件:新数组还没创建
                        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)) //帮助扩容 s=sc+1
                            transfer(tab, nt);
                    }
                    else if (U.compareAndSwapInt(this, SIZECTL, sc,
                            (rs << RESIZE_STAMP_SHIFT) + 2))  //初次扩容 将值设为很小的负数
                        transfer(tab, null);
                    s = sumCount();
                }
            }
        }
    private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
            int n = tab.length, stride; //stride步长 切割迁移数组为小份进行转移,用来设置transferIndex
            if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)   //NCPU大于1则为  n/(8*NCPU) 否则为数组长度。但要保证大于16
                stride = MIN_TRANSFER_STRIDE; // subdivide range
            if (nextTab == null) {            // initiating  //初始化nextTab,只在扩容时不为null
                try {
                    @SuppressWarnings("unchecked")
                    Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1]; //数组长度翻倍
                    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); //已迁移节点 他的hash为-1
            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)  //i >= bound 说明节点到达了界限,它的任务完成或 finish
                        advance = false;
                    else if ((nextIndex = transferIndex) <= 0) {  //倒序转移全部转移完成了
                        i = -1;// 准备退出迁移
                        advance = false;
                    }
                    else if (U.compareAndSwapInt
                            (this, TRANSFERINDEX, nextIndex,
                                    nextBound = (nextIndex > stride ?
                                            nextIndex - stride : 0))) { //将转移指针按步长递减(开始是原数组长度)
                        bound = nextBound;//界限=nextIndex(开始为原数组长度)-步长
                        i = nextIndex - 1;//i=nextIndex-1
                        advance = false;  //跳出
                    }
                }
                if (i < 0 || i >= n || i + n >= nextn) {//bound为0,i<0 或 i>=数组长度  或i+原长度>=现长度
                    int sc;                             //可能原因是原数组长度为0则i<0 ,或,或已经是最大值不能扩容?
                    if (finishing) {  //如果完成了就将nextTable清除,
                        nextTable = null;
                        table = nextTab;  //将扩容后数组作为当前数组
                        sizeCtl = (n << 1) - (n >>> 1);  //sizeCtl 为1.5 倍
                        return;   //返回
                    }
                    if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) { //将SCTL 折为 sc-1成功(帮助转移时+1)现在减回去
                        if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)  //之前正常状态 将SIZECTL 设为了 rs << RESIZE_STAMP_SHIFT) + 2
                            return;                                             //现在返回去,不等则说明其他线程还没转移完
                        finishing = advance = true;
                        i = n; // recheck before commit
                    }
                }
                else if ((f = tabAt(tab, i)) == null) //如果原tab上的某地址首节点为null 则换为 fwd
                    advance = casTabAt(tab, i, null, fwd);
                else if ((fh = f.hash) == MOVED) //如果它 hash为 -1(MOVED) 说明已被移动
                    advance = true; // already processed
                else {
                    synchronized (f)  {//首节点上锁
                        if (tabAt(tab, i) == f) { //再次确认首节点
                            Node<K,V> ln, hn;
                            if (fh >= 0) { //首节点hash大于0
                                int runBit = fh & n;  //截取hash确定位置 n是扩容前长度
                                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);//将链好的lownode首节点放入新数组低位
                                setTabAt(nextTab, i + n, hn);//将链好的hinode首节点放入新数组高位
                                setTabAt(tab, i, fwd);//把旧数组位置上hash设为-1
                                advance = true;
                            }
                            else if (f instanceof TreeBin) { //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;
                            }
                        }
                    }
                }
            }
        }
    private final Node<K,V>[] initTable() {
        Node<K,V>[] tab; int sc;
        while ((tab = table) == null || tab.length == 0) {
            if ((sc = sizeCtl) < 0) //已被别人抢先初始化了(第一个初始化的线程将sizeCtl改为-1) 进入准备状态(等待被唤醒
    Thread.yield(); // lost initialization race; just spin
    else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
                try {
                    if ((tab = table) == null || tab.length == 0) {
                        int n = (sc > 0) ? sc : DEFAULT_CAPACITY; //sizeCtl大于0就使用它的大小,等于0默认容量大小
    @SuppressWarnings("unchecked")
                        Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];//new个node数组
    table = tab = nt;
                        sc = n - (n >>> 2);//sc=0.75n
    }
                } finally {
                    sizeCtl = sc;//sizeCtl为0.75n  sizeCtl像是数组扩容阈值
    }
                break;
            }
        }
        return tab;
    }
  • 相关阅读:
    【贪心】【堆】Gym
    【并查集】Gym
    【拓扑排序】【bitset】Gym
    【递归】【线段树】【堆】AtCoder Regular Contest 080 E
    【二分图】【并查集】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem L. Canonical duel
    【动态规划】【滚动数组】【bitset】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem J. Terminal
    【二分】【字符串哈希】【二分图最大匹配】【最大流】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem I. Minimum Prefix
    【枚举】【最小表示法】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem F. Matrix Game
    【推导】【构造】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem E. Space Tourists
    【推导】【贪心】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem D. Clones and Treasures
  • 原文地址:https://www.cnblogs.com/llsblog/p/10630529.html
Copyright © 2011-2022 走看看