Why has this condition "(sc >>> RESIZE_STAMP_SHIFT) != rs" been removed in JDK12 ConcurrentHashMap?

Viewed 152

In the addCount method(also in the helpTransfer method),the first condition for stopping the capacity expansion is (sc >>> RESIZE_STAMP_SHIFT) != rs,I know there's a bug here in JDK8:probable bug in logic of ConcurrentHashMap.addCount().But in JDK12, what I'm wondering is: Why has this condition (sc >>> RESIZE_STAMP_SHIFT) != rs been removed? I think this condition should become (sc >>> RESIZE_STAMP_SHIFT) != (rs >>> RESIZE_STAMP_SHIFT) in JDK12.

In 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;
                    //...
                }
                //...
            }
        }
    }

In JDK12:

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) << RESIZE_STAMP_SHIFT;
            if (sc < 0) {
                if (sc == rs + MAX_RESIZERS || sc == rs + 1 ||
                        (nt = nextTable) == null || transferIndex <= 0)
                    break;
                //...
            } 
            //...
        }
    }
}

I've also noticed the issue of Bug in the logic of ConcurrentHashMap.addCount() when used in Threads, but it's still in open status

1 Answers

(sc >>> RESIZE_STAMP_SHIFT) != rs means that tab.length is changed after the assignment (sc = sizeCtl). In this situation, there must be another thread finished or is finishing resizing (in transfer method).

  1. When it is finishing, nextTable == null will be true.

  2. When it is finished, sizeCtl will be different from the current one for different sizeCtl in different resizing turn.

Then (U.compareAndSetInt(this, SIZECTL, sc, sc + 1)) will be false and this thread in the next iteration will break the while for sc > 0 or help the next resizing or retry.

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;
    }
...
}

Related