Some Understanding of ConcurrentHashMap under JDK1.8

In JDK1.8, ConcurrentHashMap has removed the segmented lock from the put method, instead CAS lock and synchronized

ConcurrentHashMapyes Java There is a set of key-value pairs that take into account both performance and thread security, as well as a set of key-value pairs.HashTableas well asHashMap
HashTableIs a thread-safe class because it has allpublicBoth methods aresynchronizedDecoration, which leads to a problem, is too inefficient.

althoughHashMapstayJDK1.8In concurrent scenarios, looping does not occur when the expansion is triggered, but data loss occurs.
So if you need to be multithreaded(Read more and write less))Use Map In the case of collections,ConcurrentHashMapIt's a good choice.

ConcurrentHashMapstay JDK1.8 When put()Segmented lock in methodSegmentRemove, use one insteadCASLock andsynchronizedImplement thread security for insert methods.
The following code:

/** Implementation for put and putIfAbsent */
    final V putVal(K key, V value, boolean onlyIfAbsent) {
       //Omit related code
        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) {
                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);
            else {
                V oldVal = null;
                synchronized (f) {
                    //Omit related code
                }
                if (binCount != 0) {
                    if (binCount >= TREEIFY_THRESHOLD)
                        treeifyBin(tab, i);
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
        addCount(1L, binCount);
        return null;
    }

You can see that in JDK1.8, ConcurrentHashMap is implemented directly using Array + Chain List + Red-Black Tree, and the time complexity is between O(1) and O(n). If the Chain List is converted to Red-Black Tree, then it is O(1) to O(nlogn).
It is worth mentioning here that ConcurrentHashMap determines whether tabAt (tab, I = (n - 1) & hash is null, if yes, is inserted directly using CAS, and if not empty, synchronized locks the first node of the current Node, because when the Node is not empty, it proves that a Hash collision occurs at this time, involving the addition or redness of the end node of the chain table.The addition of black tree nodes and the balance of red and black trees are naturally nonatomic.

This results in the inability to use CAS, which is sufficient when Node's current subscript is null because it only involves the addition of arrays.

Because CAS is a version-based approach and there are too many operations after a collision, it is appropriate to use synchronized directly.

The difference between ConcurrentHashMap and HashMap in iteration

When a collection dynamically adds or deletes elements during iteration, a Concurrentmodificationexception is thrown, but not in ConcurrentHashMap, such as the following code:

public static void main(String[] args) {
    Map<String,String> map = new ConcurrentHashMap<String, String>();
    map.put("a","a1");
    map.put("b","b1");
    map.put("c","c1");
    map.put("d","d1");
    map.put("e","e1");
    Iterator<String> iterator = map.keySet().iterator();
    while (iterator.hasNext()){
        String it = iterator.next();
        if("b".equals(it)){
            map.remove("d");
        }
        System.out.println(it);
    }
}

//The console prints as follows:
a
b
c
e

When you change ConcurrentHashMap to HashMap, the console throws an exception:

Exception in thread "main" a
b
java.util.ConcurrentModificationException
    at java.util.HashMap$HashIterator.nextNode(HashMap.java:1442)
    at java.util.HashMap$KeyIterator.next(HashMap.java:1466)
    at xyz.somersames.ListTest.main(ListTest.java:22)

The reason is that ConcurrentHashMap's next method does not check modCount and expectedModCount, but does it check whether the next node is empty?

  if ((p = next) == null)
    throw new NoSuchElementException();

When we remove, ConcurrentHashMap removes directly by modifying the pointer. Similarly, the head node of the array is locked until the end of the removal, so at the same time, only one thread will operate on all nodes that are subscripted to the current array.

In HashMap, however, the next method does a check, and the remove operation modCount, which results in an unequal modCount and expectedModCount, causes a
ConcurrentModificationException

Modify the code a little:

public static void main(String[] args) {
    Map<String,String> map = new ConcurrentHashMap<String, String>();
    map.put("a","a1");
    map.put("b","b1");
    map.put("c","c1");
    map.put("d","d1");
    map.put("e","e1");
    Iterator<String> iterator = map.keySet().iterator();
    while (iterator.hasNext()){
        if("b".equals(iterator.next())){
            map.remove("d");
        }
        System.out.println(iterator.next());
    }
}
//The console prints as follows:
b
d
Exception in thread "main" java.util.NoSuchElementException
    at java.util.concurrent.ConcurrentHashMap$KeyIterator.next(ConcurrentHashMap.java:3416)
    at com.xzh.ssmtest.ListTest.main(ListTest.java:25)

Concurrent Processing

Because each Node's first node is modified by synchronized, the addition of an element is converted to an atomic operation, and Node's value and next are modified by the volatile keyword to ensure visibility.

Keywords: Java less

Added by tcr480 on Sat, 25 May 2019 19:11:21 +0300