A few days ago, and chat with friends, said ConcurrentHashMap encountered a dead circulation problems, it was thought this unscientific? ConcurrentHashMap how there is an infinite loop does, after all, it has solved the problem in an infinite loop in a rehash HashMap, but with in-depth analysis and found that things are not as simple as previously thought - (jdk version based on the following analysis: jdk1.8.0_171)
Safe, not directly posted service code problem, thus simplifying the problem into the following code:
Of ConcurrentHashMap <Integer, Integer> Map = new new of ConcurrentHashMap <> (); // Map default capacity 16, reached when the number of elements (capacity - capacity >> 2) = 12 time-triggered the rehash for ( int I = 0; I <. 11; I ++ ) { map.put (I, I); } map.computeIfAbsent ( 12 is, (K) -> { // this results in an endless loop :( map.put (100, 100 ); return K; } ); // other operations
Small partners interested can run on a computer, then do not say much, at first said the reason the problem: When executed computeIfAbsent
, if the key corresponding slot is empty, then creates ReservationNode
an object (hash value RESERVED=-3
) into the current slot position and then call mappingFunction.apply(key)
to generate value, according to the position assigned to slow after the value created Node, this time to complete the computeIfAbsent
process. However, these codes mappingFunction
in the map has carried out a put operation, and triggers the operation rehash, in transfer
the time slot traversing the array in turn determines whether the slot corresponding to Node null, hash value whether MOVED = -1, hash value no greater than 0 (list structure), whether the Node type is TreeBin (red-black tree structure), hash value is determined just not RESERVED=-3
the case, thus resulting in an infinite loop problem.
Analysis here, the reason is very clear, when we believe that this may be the jdk “bug”
, so the solutions we are given is:
- If there is a rehash when
slot
the node type isReservationNode
, you can give a prompt, such as throwing an exception; - In theory,
mappingFunction
the current map should no longer update operation, but jdk does not prohibit such use can not be given, preferably under.
Finally, another friend read computeIfAbsent
comments:
1 /** 2 * If the specified key is not already associated with a value, 3 * attempts to compute its value using the given mapping function 4 * and enters it into this map unless {@code null}. The entire 5 * method invocation is performed atomically, so the function is 6 * applied at most once per key. Some attempted update operations 7 * on this map by other threads may be blocked while computation 8 * is in progress, so the computation should be short and simple, 9 * and must not attempt to update any other mappings of this map. 10 */ 11 public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
We found that, in fact, people have been aware of this problem, but also specifically comments explain. . . We still too yong too simple
ah. So far, ConcurrentHashMap infinite loop problem come to an end, or to follow coding standards, do not mappingFunction
then the current map update operation . In fact, not only in ConcurrentHashMap infinite loop scenario discussed above, the following scene will trigger, and is the same reason discussed above, the following code, the junior partner may also be interested in local run under:
1 ConcurrentHashMap<Integer, Integer> map = new ConcurrentHashMap<>(); 2 map.computeIfAbsent(12, (k) -> { 3 map.put(k, k); 4 return k; 5 }); 6 7 System.out.println(map); 8 // 其他操作
Finally, the implementation of the process followed computeIfAbsent source to points along the endless loop code above, limited space, only the main flow Code Analysis:
1 public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) { 2 if (key == null || mappingFunction == null) 3 throw new NullPointerException(); 4 int h = spread(key.hashCode()); 5 V val = null; 6 int binCount = 0; 7 for (Node<K,V>[] tab = table;;) { 8 Node<K,V> f; int n, i, fh; 9 if(Tab == null || (= n-tab.length) == 0 ) 10 Tab = initTable (); . 11 the else IF ((F = tabAt (Tab, I = (n--. 1) & H)) == null ) { 12 is the Node <K, V> = R & lt new new ReservationNode <K, V> (); 13 is synchronized (R & lt) { 14 // used herein is not synchronized significance for partial objects, primarily to ensure the following operation concurrency issues cas 15 IF (casTabAt (Tab, I, null , R & lt)) { 16 BinCount =. 1 ; . 17 the Node <K, V> Node = null ; 18 is the try { . 19 // value returned here may be null Yo 20 is IF ((Val = mappingFunction.apply (Key)) =! null ) 21 is Node = new new the Node <K, V> (H, Key, Val, null ); 22 is } the finally { 23 is setTabAt (Tab, I, Node); 24 } 25 } 26 is } 27 IF (BinCount = 0! ) 28 BREAK ; 29 } 30 the else IF ((FH = f.hash) == MOVED) 31 is Tab = helpTransfer (Tab, F); 32 the else { 33 is Boolean added = to false ; 34 is the synchronized (F) { 35 // only determines the node.hash> = 0 and the node is TreeBin type, the type is not determined `ReservationNode` 36 // determined here and the like when the expansion 37 [ IF (tabAt (Tab, I) == F) { 38 is IF (FH> = 0 ) { 39 BinCount. 1 = ; 40 for (the Node <K, V> E = F ;; ++ BinCount) { 41 is K ek; V ev; 42 if (e.hash == h && 43 ((ek = e.key) == key || 44 (ek != null && key.equals(ek)))) { 45 val = e.val; 46 break; 47 } 48 Node<K,V> pred = e; 49 if ((e = e.next) == null) { 50 if ((val = mappingFunction.apply(key)) != null) { 51 added = true; 52 pred.next = new Node<K,V>(h, key, val, null); 53 } 54 break; 55 } 56 } 57 } 58 else if (f instanceof TreeBin) { 59 binCount = 2; 60 TreeBin<K,V> t = (TreeBin<K,V>)f; 61 TreeNode<K,V> r, p; 62 if ((r = t.root) != null && 63 (p = r.findTreeNode(h, key, null)) != null) 64 val = p.val; 65 else if ((val = mappingFunction.apply(key)) != null) { 66 added = true; 67 t.putTreeVal(h, key, val); 68 } 69 } 70 } 71 is } 72 IF (! BinCount = 0 ) { 73 is IF (BinCount> = TREEIFY_THRESHOLD) 74 treeifyBin (Tab, I); 75 IF (! Added) 76 return Val; 77 BREAK ; 78 } 79 } 80 } 81 IF ( ! Val = null ) 82 // counting statistics + & expansion threshold determination operation 83 addCount (1L , BinCount); 84 return Val; 85 }
Recommended reading:
More articles can scan the following QR code: