In JDK1.8, ConcurrentHashMap has removed the segmented lock from the put method, instead CAS lock and synchronized
ConcurrentHashMap
yes 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.HashTable
as well asHashMap
,HashTable
Is a thread-safe class because it has allpublic
Both methods aresynchronized
Decoration, which leads to a problem, is too inefficient.although
HashMap
stayJDK1.8
In 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,ConcurrentHashMap
It's a good choice.
ConcurrentHashMap
stay JDK1.8 When put()Segmented lock in methodSegment
Remove, use one insteadCAS
Lock andsynchronized
Implement 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.