java 8 ConcurrentHashMap源码解析

简介

java(版本:1.8.0_161) 中ConcurrentHashMap主要在多线程中使用,dev不需要自己保证数据同步。用法基本上和HashMap相似。本文主要讲解源码中的put方法是怎么工作的。让我们带着问题去看源码。

问题

1. ConcurrentHashMap是如何保证多个线程同时调用put方法时而不出现错误的

2.ConcurrentHashMap是如何进行扩容的。当ConcurrentHashMap正在扩容的时候,当多个线程同时调用put怎么办

3.ConcurrentHashMap是怎么维护元素数量的, 当多个线程同时调用put的时候,ConcurrentHashMap是怎么维护元素的数量的

4.ConcurrentHashMap如何初始化table,当多个线程同时初始化时怎么办

源码

先解释一个很重要的属性

sizeCtl

    正数代表threshold(当前ConcurrentHashMap容量的0.75倍)

    负数代表正在进行初始化或扩容操作

   -1代表正在初始化

   -N 表示有N-1个线程正在进行扩容操

下面讲解put方法。

当调用put的时候,直接调用了putVal方法

public V put(K key, V value) {
    return putVal(key, value, false);
}

putVal主要用于把key-value插入table中

// putVal用于将key-value放入table中
final V putVal(K key, V value, boolean onlyIfAbsent) {
    if (key == null || value == null) throw new NullPointerException();
    // spread将hash值的高16位和低16位进行异或操作,这样高16位和低16位就都可以参与进hash的计算了
    int hash = spread(key.hashCode());
    int binCount = 0;
    // 当第一次调用put方法的时候,table是null
    for (Node<K,V>[] tab = table;;) {
        Node<K,V> f; int n, i, fh;
        if (tab == null || (n = tab.length) == 0)
            tab = initTable(); // 第一次调用的时候,初始化table(initTable方法下面讲)
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { // 当hash所在bin是null的时候,尝试将key-value放入bin
            if (casTabAt(tab, i, null,
                         new Node<K,V>(hash, key, value, null)))
                break;                   // no lock when adding to empty bin
        }

        // 当hash == MOVED,说明table正在进行扩容操作
        else if ((fh = f.hash) == MOVED)
            tab = helpTransfer(tab, f); // 帮助table进行扩容
        else {
            V oldVal = null;
            synchronized (f) { // 锁住bin的第一个node,就相当于锁住了整个bin
                if (tabAt(tab, i) == f) {
                    if (fh >= 0) { // 如果是bin,就将node放入bin里面(bin的末尾)
                        binCount = 1;
                        for (Node<K,V> e = f;; ++binCount) {
                            K ek;
                            if (e.hash == hash &&
                                ((ek = e.key) == key ||
                                 (ek != null && key.equals(ek)))) {
                                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) { // 是bin
                if (binCount >= TREEIFY_THRESHOLD) // bin里元素的个数超过TREEIFY_THRESHOLD,就将bin弄成红黑树
                    treeifyBin(tab, i);
                if (oldVal != null)
                    return oldVal;
                break;
            }
        }
    }
    addCount(1L, binCount);//给table中的元素的数量加一
    return null;
}

initTable用于初始化一个table

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时,代表有其他线程正在初始化table,让出CPU让其他线程进行初始化操作
            Thread.yield();
        else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) { // 将sizeCtl置为-1,代表正在初始化table,从而告诉其他线程正在初始化
            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);
                }
            } finally {
                sizeCtl = sc; // 当初始化完成,将sizeCtl置为threshold(capacity*0.75)
            }
            break;
        }
    }
    return tab;
}

addCount主要有两个作用.

 1.给table中元素的数量加一

2.检查是否需要扩容,如果需要则进行扩容

private final void addCount(long x, int check) {
    CounterCell[] as; long b, s;
    /**
     * 这里有两种情况
     * 1.counterCells是空
     *   1).尝试将给baseCount加一
     *   2).如果失败(由于多个线程再改变baseCount的值),初始化counterCells并且把一加到counterCells里
     * 2.counterCells不是空
     *   直接把一加到counterCells里
     */
    if ((as = counterCells) != null ||
        !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))) { // 尝试把一加到counterCells里
            
            // fullAddCount用于将一加到counterCells里
            fullAddCount(x, uncontended);
            return;
        }
        if (check <= 1)
            return;
        s = sumCount();
    }

    // 对table进行扩容操作
    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);

            // 其他线程正在进行扩容操作
            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();
        }
    }
}

fullAddCount主要用于维护table中node的数量

private final void fullAddCount(long x, boolean wasUncontended) {
    int h;
    if ((h = ThreadLocalRandom.getProbe()) == 0) {
        ThreadLocalRandom.localInit();      // force initialization
        h = ThreadLocalRandom.getProbe();
        wasUncontended = true;
    }
    boolean collide = false;                // True if last slot nonempty
    /**
     * 1. if counterCells is not null and length is more than 0
     *    1) when the counterCell which we hope to add into is null, will set cellsBusy as 1 and init the null counterCell
     *    2) when the counterCell is not null, try to add 1L into to the counterCell
     *    3) if we try to add several time but failed, will try add increase the capacity of CounterCells
     * 
     * 2. if counterCells is not init(counterCells is null or length is 0)
     *    1) set cellsBusy as 1(tell other threads, we are initing) 
     *    2) init CounterCells and put CounterCell into it
     *    3) change cellsBusy to 0
     * 
     * 3. if other threads are initing the CounterCells, we will try to add 1L to baseCount
     */
    /**
     * 1.如果 counterCells不是null,且长度大于0且没有其他线程再操作counterCells(意思就是counterCells已经完全初始化好了)
     *   1)当counterCell是空的时候,将cellsBusy设置成1,然后初始化
     *   2)当counterCell不是空,将尝试将x加到counterCell
     *   3)如果我们尝试了几次都失败了,就会对将cellsBusy设置成1,然后counterCells进行扩容
     * 2.couterCells没有初始化且没有其他线程再操作counterCells
     *   将cellsBusy设置成1,然后进行初始化操作
     * 3.其他线程正在初始化counterCells, 就尝试着将x加到baseCount
     */
    for (;;) {
        CounterCell[] as; CounterCell a; int n; long v;
        if ((as = counterCells) != null && (n = as.length) > 0) {
            if ((a = as[(n - 1) & h]) == null) {
                if (cellsBusy == 0) {            // Try to attach new Cell
                    CounterCell r = new CounterCell(x); // Optimistic create
                    if (cellsBusy == 0 &&
                        U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
                        boolean created = false;
                        try {               // Recheck under lock
                            CounterCell[] rs; int m, j;
                            if ((rs = counterCells) != null &&
                                (m = rs.length) > 0 &&
                                rs[j = (m - 1) & h] == null) {
                                rs[j] = r;
                                created = true;
                            }
                        } finally {
                            cellsBusy = 0;
                        }
                        if (created)
                            break;
                        continue;           // Slot is now non-empty
                    }
                }
                collide = false;
            }
            else if (!wasUncontended)       // CAS already known to fail
                wasUncontended = true;      // Continue after rehash
            else if (U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))
                break;
            else if (counterCells != as || n >= NCPU)
                collide = false;            // At max size or stale
            else if (!collide)
                collide = true;
            else if (cellsBusy == 0 &&
                     U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
                try {
                    if (counterCells == as) {// Expand table unless stale
                        CounterCell[] rs = new CounterCell[n << 1];
                        for (int i = 0; i < n; ++i)
                            rs[i] = as[i];
                        counterCells = rs;
                    }
                } finally {
                    cellsBusy = 0;
                }
                collide = false;
                continue;                   // Retry with expanded table
            }
            h = ThreadLocalRandom.advanceProbe(h);
        }
        else if (cellsBusy == 0 && counterCells == as &&
                 U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
            boolean init = false;
            try {                           // Initialize table
                if (counterCells == as) {
                    CounterCell[] rs = new CounterCell[2];
                    rs[h & 1] = new CounterCell(x);
                    counterCells = rs;
                    init = true;
                }
            } finally {
                cellsBusy = 0;
            }
            if (init)
                break;
        }
        else if (U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x))
            break;                          // Fall back on using base
    }
}

transfer主要用于扩容

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; // subdivide range
    // 初始化nextTable
    if (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; // 旧table的长度
    }
    int nextn = nextTab.length;
    ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab); //ForwardingNode用于表明这个bin已经完成扩容
    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
                     (this, TRANSFERINDEX, nextIndex,
                      nextBound = (nextIndex > stride ?
                                   nextIndex - stride : 0))) {
                bound = nextBound;
                i = nextIndex - 1;
                advance = false;
            }
        }
        if (i < 0 || i >= n || i + n >= nextn) {
            int sc;
            if (finishing) {//扩容完成后,将sizeCtl回复位capacity*0.75
                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)//如果正在扩容的bin是空的,就把它标记成历经扩容完成
            advance = casTabAt(tab, i, null, fwd);
        else if ((fh = f.hash) == MOVED)//bin已经被处理过了,进行下一个bin的扩容
            advance = true;
        else {
            synchronized (f) { // 锁住bin的第一个节点进行扩容操作
                /**
                 * 假如旧的table的长度是16,新table的长度是32.
                 * 当扩容的时候旧table的第0个bin,对应的新table的第0个或者第16个bin. 如果最高为是1,则说明node在高16位(16~31),最高为是0,则说明在低16位(0~15)
                 * lastRun是用来标识是不是一个bin里的所有元素都是高16位或者都是低16位,
                 * 如果for循环之后,lastRun == f(bin里的第一个node)则说明一个bin里的所有元素都是高16位或者都是低16位
                 * 否则说明一个bin里的所有元素不都是高16位或者都是低16位
                 * 
                 * 给bin扩容的思想是:将旧table的bin里的元素,如果是高位,就放到新table的高位,如果是低位,就放到新table的低位,
                 */
                if (tabAt(tab, i) == f) {
                    Node<K,V> ln, hn;
                    if (fh >= 0) {
                        int runBit = fh & 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);
                        setTabAt(nextTab, i + n, hn);
                        setTabAt(tab, i, fwd); // 扩容完成,将旧table的bin的节点设为ForwardingNode
                        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;
                    }
                }
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/majinliang1234/article/details/80612596