Agustin Treceno
Agustin Treceno

Reputation: 351

Updating (and Iterating) a Map in Java

I have seen this code in the book Java 7 recipes to update and iterate a Map concurrently:

ConcurrentMap<Integer,String> concurrentMap = new ConcurrentHashMap<Integer, String>();
for (int i =0;i < 1000;i++) {
  startUpdateThread(i, concurrentMap);
}
try {
  Thread.sleep(1000);
} catch (InterruptedException e) {
  e.printStackTrace();
}
for (Map.Entry<Integer, String> entry : concurrentMap.entrySet()) {
  System.out.println("Key :"+entry.getKey()+" Value:"+entry.getValue());
}

And the method to update the map as follows:

private void startUpdateThread(int i, final ConcurrentMap<Integer, String> concurrentMap) {
    Thread thread = new Thread(new Runnable() {
        public void run() {
            while (!Thread.interrupted()) {
                int randomInt = random.nextInt(20);
                concurrentMap.put(randomInt, UUID.randomUUID().toString());
            }
} });
    thread.setName("Update Thread "+i);
    updateThreads.add(thread);
    thread.start();
}

I have tried with just a HashMap instead of ConcurrentHasMap and the results are the same (monitored with Java VisualVM). Does anyone know why?

Thanks, Agustin

UPDATE: A few interesting questions:

  1. If the HashMap has a constant capacity, what of the following operations can be performed safely?
    • Two threads updating values in the HashMap.
    • Two threads updating values in the HashMap while a third one is reading the HashMap.
  2. If I want speed in updating a Map, does it make sense to have more than 4 threads in a computer with only 4 processors?

Upvotes: 0

Views: 179

Answers (2)

Ben Barkay
Ben Barkay

Reputation: 5622

ConcurrentHashMap allows for multiple-thread access, in contrast to HashMap which does not.

Calling HashMap#put from multiple threads at the same time may break your map. ConcurrentHashMap handles those scenarios and mitigates race conditions.

As for your test in particular, your map only has 20 keys which means it will get full relatively quickly. The weak spot for hashmap is when you need to expand the bucket space and at the same time put another entry. Try increasing the amount of keys to Integer.MAX_VALUE and you will have a much higher chance to see it breaking.

Upvotes: 2

Andrey Chaschev
Andrey Chaschev

Reputation: 16516

The code below will give you a different result. The reason why your code does not crash is a small random interval of 20 integers. So your map gets full instantly and after this it's key set is not being modified. I changed the random interval to 10000 and added sleeps, so now it's being continuously updated during the program run and crashes for HashMap. And it works for ConcurrentHashMap which is capable to iterate over the data which is being modified.

public class Test {
    static List<Thread> threadList = new ArrayList<>();

    public static void main(String[] args) throws InterruptedException {
        Map<Integer,String> map = new HashMap<>();
        for (int i =0;i < 1000;i++) {
            startUpdateThread(i, map);
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        for (Map.Entry<Integer, String> entry : map.entrySet()) {
            System.out.println("Key :"+entry.getKey()+" Value:"+entry.getValue());
        }

        for (Thread thread : threadList) {
            thread.interrupt();
        }
    }

    private  static void startUpdateThread(int i, final Map<Integer, String> concurrentMap) {
        Thread thread = new Thread(new Runnable() {
            public void run() {
                Random random = new Random();
                while (true) {
                    int randomInt = random.nextInt(10000);
                    concurrentMap.put(randomInt, UUID.randomUUID().toString());
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        return;
                    }
                }
            } });

        threadList.add(thread);

        thread.setName("Update Thread " + i);
        thread.start();
    }
}

Upvotes: 1

Related Questions