为什么在JDK12 ConcurrentHashMap中删除了这种条件“sc >>> RESIZE_STAMP_SHIFT!= rs”?

问题描述

在addCount方法(也包括helpTransfer方法)中,停止容量扩展的第一个条件是(sc >>> RESIZE_STAMP_SHIFT) != rs,我知道JDK8中存在一个错误probable bug in logic of ConcurrentHashMap.addCount()。但是在JDK12中,我想知道的是:为什么要删除此条件(sc >>> RESIZE_STAMP_SHIFT) != rs?我认为这种情况在JDK12中应该变成(sc >>> RESIZE_STAMP_SHIFT) != (rs >>> RESIZE_STAMP_SHIFT)

在JDK8中:

private final void addCount(long x,int check) {
        //...
        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;
                    //...
                }
                //...
            }
        }
    }

在JDK12中:

private final void addCount(long x,int check) {
    //...
    if (check >= 0) {
        Node<K,nt;
        int n,sc;
        while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
                (n = tab.length) < MAXIMUM_CAPACITY) {
            int rs = resizeStamp(n) << RESIZE_STAMP_SHIFT;
            if (sc < 0) {
                if (sc == rs + MAX_RESIZERS || sc == rs + 1 ||
                        (nt = nextTable) == null || transferIndex <= 0)
                    break;
                //...
            } 
            //...
        }
    }
}

我也注意到了Bug in the logic of ConcurrentHashMap.addCount() when used in Threads的问题,但是它仍然处于打开状态

解决方法

(sc >>> RESIZE_STAMP_SHIFT) != rs 表示 tab.length 在赋值 (sc = sizeCtl) 后发生变化。在这种情况下,必须有另一个线程完成或正在完成调整大小(在传输方法中)。

  1. 完成后,nextTable == null 将为 true

  2. 当它完成时,sizeCtl 将与当前的不同,不同的 sizeCtl 在不同的调整轮次。

然后 (U.compareAndSetInt(this,SIZECTL,sc,sc + 1)) 将是假的,下一次迭代中的此线程将中断 sc > 0 的 while 或帮助下一次调整大小或重试。

private final void transfer(Node<K,V>[] tab,Node<K,V>[] nextTab) {
...

    if (finishing) {
        nextTable = null;
        // table changed after "nextTable = null"。
        table = nextTab;
        sizeCtl = (n << 1) - (n >>> 1);
        return;
    }
...
}