Learn ConcurrentHashMap concurrent write mechanism

1 Introduction

On the article stresses the implementation Unsafe class CAS, in fact, it is laying the foundation for this article. Not familiar with the venue and small partners to achieve Unsafe in the CAS . This article is mainly based on OpenJDK8do-source resolution.

2. Source

ConcurrentHashMap HashMap based implementation.

JDK1.7 and JDK1.8 as a concurrent container in the realization there is a difference. Segment lock segment by JDK1.7 achieved, JDK1.8 CAS + synchronized achieved.

2.1 ConcurrentHashMap several important ways

ConcurrentHashMap used in a method unSafe, to ensure the safety of the concurrent processing operation by way of direct memory using a hardware security mechanisms.

    private static final sun.misc.Unsafe U;
    private static final long SIZECTL;
    private static final long TRANSFERINDEX;
    private static final long BASECOUNT;
    private static final long CELLSBUSY;
    private static final long CELLVALUE;
    private static final long ABASE;
    private static final int ASHIFT;

    static {
        try {
            U = sun.misc.Unsafe.getUnsafe();
            Class<?> k = ConcurrentHashMap.class;
            SIZECTL = U.objectFieldOffset
                (k.getDeclaredField("sizeCtl"));
            TRANSFERINDEX = U.objectFieldOffset
                (k.getDeclaredField("transferIndex"));
            BASECOUNT = U.objectFieldOffset
                (k.getDeclaredField("baseCount"));
            CELLSBUSY = U.objectFieldOffset
                (k.getDeclaredField("cellsBusy"));
            Class<?> ck = CounterCell.class;
            CELLVALUE = U.objectFieldOffset
                (ck.getDeclaredField("value"));
            Class<?> ak = Node[].class;
            ABASE = U.arrayBaseOffset(ak);
            int scale = U.arrayIndexScale(ak);
            if ((scale & (scale - 1)) != 0)
                throw new Error("data type scale not a power of two");
            ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
        } catch (Exception e) {
            throw new Error(e);
        }
    }

    static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
        return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
    }

    // CAS 将Node插入bucket
    static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
                                        Node<K,V> c, Node<K,V> v) {
        return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
    }

    static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
        U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
    }

2.2 put () process

Or the old rules, the first on the flowchart to help read the source code.

The main source as follows:

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


    /** Implementation for put and putIfAbsent */
    final V putVal(K key, V value, boolean onlyIfAbsent) {
        if (key == null || value == null) throw new NullPointerException();
        int hash = spread(key.hashCode());
        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();
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { // 如果bucket==null,即没有hash冲突,CAS插入
                if (casTabAt(tab, i, null,
                             new Node<K,V>(hash, key, value, null)))
                    break;                   // no lock when adding to empty bin
            }
            // 如果在进行扩容操作,则先扩容
            else if ((fh = f.hash) == MOVED)
                tab = helpTransfer(tab, f);
            // 否则,存在hash冲突
            else {
                V oldVal = null;
                // 加锁同步
                synchronized (f) {
                    if (tabAt(tab, i) == f) {
                        if (fh >= 0) {
                            binCount = 1;
                            for (Node<K,V> e = f;; ++binCount) {
                                K ek;
                                // 遍历过程中出现相同key直接覆盖
                                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) {
                    if (binCount >= TREEIFY_THRESHOLD)
                        treeifyBin(tab, i);
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
        addCount(1L, binCount);
        return null;
    }

Put operation process is as follows:

  • If you do not initialize on the first call initTable () method to perform the initialization process
  • If no hash conflict directly insert CAS
  • If the operation is still in progress on the first expansion for expansion
  • If the hash is a conflict, it is locked to ensure the security thread, there are two cases, one is directly linked list traversal on to the end of insertion, one is a red-black tree is inserted in accordance with the red-black tree structure
  • If the number of the last list is greater than a threshold value 8, it will be converted to black mangrove configuration, break into circulation again
  • If you added successfully invoked addCount () method of statistical size, and check whether you need expansion

2.3 ConcurrentHashMap storage structure

Schematic view from below of the network

3. Conclusion

This article only analyzes ConcurrentHashMapthe put()methods and did not get (), expansion, and other methods to delete a node analysis. The main purpose is to ensure that a preliminary understanding ConcurrentMap design ideas concurrent writes. At this point, the end of this article, thanks for reading! Welcome to public attention when I met you [number].

Guess you like

Origin www.cnblogs.com/idea360/p/12501225.html