javamultithreadingconcurrencyhashmapconcurrenthashmap

Why does concurrent put to a ConcurrentHashMap not produce the expected result?


I am trying to test concurrent puts to a ConcurrentHashMap using multiple threads, but the size of the map after processing is not what I expected.

I have the following code to insert 1000 entries into a ConcurrentHashMap:

@Test
public void testThreadSafetyConcurrentHashMap() {
    Map<Integer, Integer> map = new ConcurrentHashMap<>();

    Runnable runnable = () -> {
        for (int i = 0; i < 1000; i++) {
            map.put(i, i);
        }
    };

    ExecutorService executorService = Executors.newFixedThreadPool(4);
    for (int i = 0; i < 4; i++) {
        executorService.submit(runnable);
    }

    System.out.println(map.size());
}

I am expecting 1000 items for the map.size() call, but I don't get this every time.

Can someone tell me what the problem is? I thought 4 threads putting 1000 items at the same time, would result in a total of 1000 items at the end?


Solution

  • You can use ExecutorService.invokeAll() to ensure that all threads are done with their job before the size of the map would be checked.

    This method returns "a list of Futures holding their status and results when all complete", i.e. it is blocking and that's what we need in this case (we are not interested in its return value and it's omitted in the code below).

    invokeAll() expects a collection of Callable and we can use Executors.callable() to convert a Runnable into a Callable.

    Map<Integer, Integer> map = new ConcurrentHashMap<>();
        
    Runnable runnable = () -> {
        for (int i = 0; i < 1000; i++) {
            map.put(i, i);
        }
    };
        
    ExecutorService executorService = Executors.newFixedThreadPool(4);
    
    executorService.invokeAll(Collections.nCopies(4, Executors.callable(runnable)));
        
    System.out.println(map.size());