Search code examples
javaconcurrenthashmap

inserting in a concurrent hash map


The following piece of code does not work as expected. When I call put method in Does class the value in the concurrent hash map is associated with different instances of map. So what I am trying to do is multiple threads access the same map and insert a value for the same key. However if I add a synchronized keyword to the put method it works. What am I missing?

class Does implements Runnable {
    C2 c2;

    Does(C2 c2) {
        this.c2 = c2;
    }

    public void run() {
        c2.put("Hello");
    }

}

public class C2 {
    public ConcurrentHashMap<String, List<String>> map = new ConcurrentHashMap<String, List<String>>();

    public static void main(String args[]) {
        C2 c2 = new C2();
        new Thread(new Does(c2)).start();
        new Thread(new Does(c2)).start();
        new Thread(new Does(c2)).start();
        new Thread(new Does(c2)).start();

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        List<String> lu = c2.map.get("Hello");

        System.out.println(lu);
    }

    /**
     * @param string
     */
    public void put(String string) {
        if (map.containsKey(string)) {
            List<String> li = map.get(string);
            li.add("adding something");
        } else {
            List<String> li = new ArrayList<String>();
            li.add("adding something");
            map.put(string, li);
        }
    }

}

Appreciate the help.


Solution

  • This code is not thread safe

    public void put(String string) {
        if (map.containsKey(string)) {
            // anything can happen in another thread here
            List<String> li = map.get(string);
            // anything can happen in another thread here
            li.add("adding something");
        } else {
            // anything can happen in another thread here
            List<String> li = new ArrayList<String>();
            li.add("adding something");
            // anything can happen in another thread here
            map.put(string, li);
        }
    

    In Java 8 you can use computeIfAbsent.

    public void put(String string) {
        // all most thread safe.
        map.computeIfAbsent(string, k -> new ArrayList<>())
           .add("add something");
    }
    

    Note this is still not thread safe as the ArrayList is not thread safe, so what you need is

    public void put(String string) {
        List<String> list = map.computeIfAbsent(string, k -> new ArrayList<>());
        synchronized(list) {
           list.add("add something");
        }
    }