JDK-8078726 : Unexpected behavior of ConcurrentHashMap#entrySet().removeIf(predicate)
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util.concurrent
  • Affected Version: 8u45
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_7
  • CPU: x86_64
  • Submitted: 2015-04-27
  • Updated: 2015-04-28
  • Resolved: 2015-04-28
Related Reports
Duplicate :  
Description
A DESCRIPTION OF THE REQUEST :
Collection#removeIf method can be used to remove all the elements from the collection that are satisfying a given predicate. ConcurrentHashMap is often used by multiple threads at the same time and is expected to work as expected in that kind of environment. 
Unfortunately when both of them are used at the same time unexpected things can happen. Calling ConcurrentHashMap.entrySet().removeIf(...)  can sometimes remove things from that map that are not satisfying the predicate. It happens when one thread is trying to remove items from the map using removeIf(predicate) and another thread concurrently puts new value to the map (values that are not satisfying predicate and should not be removed). 
The test case below starts two threads. One of them (DELETING_THREAD) removes all entries mapped to 'false' boolean value. Another one (ADDING_THREAD) randomly puts (1, true) or (1,false) values into the map. If it puts true in the value it expects that the entry will still be there when checked and throws an exception if it is not. It throws an exception quickly when I run it locally. That means that another thread removed (1,true) value even though it wasn't supposed to. 




JUSTIFICATION :
I think that mentioned behavior is not intuitive for ConcurrentHashMap. That class is expected to perform well in case of multithreaded environment and usually does the job really well. EntrySetView gets its removeIf method from the Collection interface (it is a default method there) and probably the implementation is just not prepared to behave well with ConcurrentHashMap. 


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
It should not be possible for ConcurrentHashMap#entrySet()#removeIf(predicate) to remove enties from the map that are not meeting the predicate. 
ACTUAL -
Actually the method may remove items that are not meeting the predicate if they are concurrently put into the map during the method execution. 

---------- BEGIN SOURCE ----------
package test;

import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;

public class MainClass {

    private static final Random RANDOM = new Random();

    private static final ConcurrentHashMap<Integer, Boolean> MAP = new ConcurrentHashMap<Integer, Boolean>();

    private static final Integer KEY = 1;

    private static final Thread DELETING_THREAD = new Thread() {

        @Override
        public void run() {
            while (true) {
                MAP.entrySet().removeIf(entry -> entry.getValue() == false);
            }
        }

    };

    private static final Thread ADDING_THREAD = new Thread() {

        @Override
        public void run() {
            while (true) {
                boolean val = RANDOM.nextBoolean();

                MAP.put(KEY, val);
                if (val == true && !MAP.containsKey(KEY)) {
                    throw new RuntimeException("TRUE value was removed");
                }

            }
        }

    };

    public static void main(String[] args) throws InterruptedException {
        DELETING_THREAD.setDaemon(true);
        ADDING_THREAD.start();
        DELETING_THREAD.start();
        ADDING_THREAD.join();
    }
}
---------- END SOURCE ----------


Comments
1) Run the attached test case MainClass.java with 8u31, 8u45, 8u60 ea b10. 2) Result Table (Windows 7 (64-bit): 8u31: FAIL 8u45: FAIL 8u60 ea b10: FAIL Output: ----------------------------------------------------------------------------------------------------- 8u45: > java MainClass Exception in thread "Thread-1" java.lang.RuntimeException: TRUE value was removed at javaapplication271.MainClass$2.run(MainClass.java:34) BUILD SUCCESSFUL (total time: 0 seconds) ----------------------------------------------------------------------------------------------------- ________________________________________________________________________________ Result: The issue is reproducible with the provided test case as explained by the submitter.
28-04-2015