简体   繁体   中英

Copying ConcurrentHashMap to HashMap inconsistency

im trying to do the following:

  1. My program starts threads, which increment an AtomicInteger from ConcurrentHashMap<String, AtomicInteger> and adds new Integer to ConcurrentHashMap<String, CopyOnWriteArrayList<Integer>> . In that case, the size of the CopyOnWriteArrayList is equal to the Value of the AtomicInteger (of course for entries with the same key)
  2. After all threads are done (when the CountDownLatch is finished) I try to convert ConcurrentHashMap<String, AtomicInteger> into HashMap<String, Integer> for sorting that Map by Value, because AtomicInteger is not comparable.
  3. After conversion I sort the HashMap by the Values and pick 20 entries with the highest value - in the sorted Map they are the first 20 entries.
  4. Finally I pack the Values in an List and make JSON String with GSON.

Issue

What I expect: due my usage of AtomicInteger , ConcurrentHashMap and CopyOnWriteArrayList I expect that all sizes and values for all entries with the same key are equal, even in my JSON String like:

myAtomcIntegerConcurrentHashMap.get("ABC").intValue() == 
myCOWArrayListConcurrentHashMap.get("ABC").size() == 
myNewHashMap.get("ABC")

But result seems be different. I made some console outputs to test my Values and got the following:

While copying from ConcurrentHashMap to HashMap I verify my values again. Every time the "bad copied" values are different (for the code snippet look below):

 COWArrayList.size  AtomicInteger.intValue  Value in new HashMap
 299                299                     298
 122                122                     121

After that I iterate 4 more times over my new HashMap to compare my values again and every time I get new random "bad copied" values (notice, the values were not detected while copying) (for the code snippet look below):

 COWArrayList.size  AtomicInteger.intValue  Value in new HashMap  Common Key
 849                849                     827                   CGCCACC
 838                838                     813                   GGTGGTG

My Json is also incorrect. Eg for the Key "CGCCACC" Size of my Array in Json is 887 , not like in the Table above ( 849 ).

Here are code snippets I use (Some of them are from StackOverflow):

Incrementing AtomicInteger and adding new Integer to CopyOnWriteArrayList in my Threads:

//Add new Integer 'position' to the COWArrayList from 'positions' with the key 'frame'
List<Integer> copyArr = positions.get(frame);
if (copyArr == null) {
  copyArr = new CopyOnWriteArrayList<Integer>();
  List<Integer> inMap = positions.putIfAbsent(frame, (CopyOnWriteArrayList<Integer>) copyArr);
  if (inMap != null) copyArr = inMap; // already in map
}
copyArr.add(position);

//Increment the AtomicInteger from 'scores' with the key 'frame'
AtomicInteger value = scores.get(frame);
if (value==null){ 
  value = new AtomicInteger();
  AtomicInteger actual = scores.putIfAbsent(frame, value);
  if(actual != null) value = actual;
}
value.incrementAndGet();

Copy from ConcurrentHashMap<String, AtomicInteger> to the HashMap<String, Integer> each value (I guess its very inefficient) and immediate verifying:

//init new, non-concurrent Map
Map<String, Integer> myHashMap = new HashMap<String, Integer>();

//iterate over the Map and copy each value from 'scores' to 'newHashMap'
for(Map.Entry<String, AtomicInteger> score : scores.entrySet()){
  myHashMap.put(score.getKey(), score.getValue().intValue());

  //verify just added Value and print values of all Maps if something is wrong
  if(score.getValue().intValue() != myHashMap.get(score.getKey())){
    System.out.println(score.getValue().intValue() + " " + positions.get(score.getKey()).size() + " " + myHashMap.get(score.getKey()));
  }
}

Verifying copied values of myHashMap again (here I also get random "bad copied" values):

for(Map.Entry<String, AtomicInteger> score : scores.entrySet()){
  if(score.getValue().intValue() != myHashMap.get(score.getKey())){
  System.out.println(score.getValue().intValue() + " = " + positions.get(score.getKey()).size() + " =? " + myHashMap.get(score.getKey()));
  }
}

Why could that happen, have I missed something in my logic?

For further information / code etc. - just ask.

Thank you for helping me!

It seems that AtomicInteger ques by calling AtomicInteger.incrementAndGet() an increment event. My threads are calling that function ~3.5 Mio times and the Queue is huge. After my Threads are done, the main Thread instantly copies the ConcurrentHashMap of AtomicInteger , but the queue of some AtomicIntegers is not completley done. That makes the inconsistency.

I solved that by giving the AtomicInteger some time to finish it "increment Queue" after all threads are done.

    try {
        myCountDownLatch.await();
        Thread.sleep(5000); //let AtomicInteger finish they incrementations
    } catch (Exception e) {
        System.err.println("Error in a Latch Countdown: " + e.toString());
    }

And voila! There no inconsistency anymore!

If there is another method to wait for AtomicInteger finish their increment Queues, I would be thankful to read about that.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM