Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the code hang with HashMap.put() from multiple threads?

I've been trying to prove there's a bug in the application by creating a simple unit test which is putting values to the map. I was expecting ConcurrentModificationException, but all I got was hanging threads in the executor and I don't see where exactly is the problem.

The test is here:

@Test
public void testHashMap() throws Exception {
    final Random rnd = new Random();
    final Map<String, Object> map = new HashMap<>();
    ExecutorService executor = Executors.newFixedThreadPool(10);
    for (int i = 0; i < 100; i++) {
        final int counter=i;
        executor.execute(new Runnable() {
            @Override
            public void run() {
                try{
                    for (int j = 0; j<1000; j++){
                        map.put(String.valueOf(rnd.nextLong()), new Object());
                        //map.put("A", new Object());
                    }
                    System.out.println("Thread "+counter+" finished");
                }catch(Exception e){
                    System.out.println("Thread "+counter+" failed with exception: ");
                    e.printStackTrace();
                }
            }
        });
    }
    executor.shutdown();
    int i = 0;
    while (!executor.isTerminated()) {
        i++;
        Thread.sleep(1000);
        System.out.println("Waited "+i+" seconds");
    }
}

I know that I shouldn't do this, but I don't understand why am I not getting exception and why the threads just hang there? When I do a simple put on the map(the commented code), then it passes fine.

Here is the sample output:

Thread 0 finished
Thread 1 finished
Thread 4 finished
Thread 2 finished
Thread 5 finished
Thread 7 finished
Thread 9 finished
Thread 10 finished
Thread 13 finished
Thread 6 finished
Thread 14 finished
Thread 8 finished
Thread 12 finished
Thread 16 finished
Thread 19 finished
Thread 20 finished
Thread 21 finished
Thread 26 finished
Thread 25 finished
Thread 24 finished
Thread 28 finished
Thread 3 finished
Thread 31 finished
Thread 30 finished
Thread 32 finished
Thread 34 finished
Thread 35 finished
Thread 36 finished
Thread 37 finished
Thread 38 finished
Thread 39 finished
Thread 22 finished
Thread 27 finished
Thread 42 finished
Thread 43 finished
Thread 41 finished
Thread 45 finished
Thread 44 finished
Thread 47 finished
Thread 48 finished
Thread 49 finished
Waited 1 seconds
Waited 2 seconds
Waited 3 seconds
Waited 4 seconds
Waited 5 seconds
...indefinitely
like image 215
NeplatnyUdaj Avatar asked Apr 08 '14 18:04

NeplatnyUdaj


1 Answers

Why does the code hang with HashMap.put() from multiple threads?

You cannot use a HashMap with multiple threads without external synchronization. You should switch to use ConcurrentHashMap.

You could also use the Collections.synchronizedMap(new HashMap<>()); but the ConcurrentHashMap should give better performance.

I was expecting ConcurrentModificationException, but all I got was hanging threads in the executor and I don't see where exactly is the problem.

You are seeing a hang because one of the threads has a corrupted version of the HashMap -- probably a looped linked list where two hash entries are linked to each other or something. If you do a thread dump you will see that it is spinning while traversing the HashMap entries.

The ConcurrentModificationException is only thrown if the HashMap class detects a modification. Typically this is in single-threaded usage when you (for example) remove an entry by calling map.remove(...) instead of iterator.remove() while iterating across the map.

Synchronization does two important things: mutex locking and memory synchronization. Each processor has its own memory cache and threads can easily see partially synchronized views of an object (your HashMap in this case) without proper memory synchronization.

The only thing which is quite frustrating is that sometimes it throws an exception, but most of the time it just hangs.

In multithreaded situations, there are tons of race conditions by definition since the threads are usually running in parallel on multiple processors. It's very hard to predict, given the parallel nature of the environment, on the type of failure.

like image 150
Gray Avatar answered Oct 17 '22 06:10

Gray