ConcurrentHashMap it has a infinite loop problem?

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 ReservationNodean 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 computeIfAbsentprocess. However, these codes mappingFunctionin the map has carried out a put operation, and triggers the operation rehash, in transferthe 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=-3the 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:

  1. If there is a rehash when slotthe node type is ReservationNode, you can give a prompt, such as throwing an exception;
  2. In theory, mappingFunctionthe current map should no longer update operation, but jdk does not prohibit such use can not be given, preferably under.

Finally, another friend read computeIfAbsentcomments:

 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 simpleah. So far, ConcurrentHashMap infinite loop problem come to an end, or to follow coding standards, do not mappingFunctionthen 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:

 

Guess you like

Origin www.cnblogs.com/luoxn28/p/11068316.html