Search code examples
javamultithreadingconcurrenthashmapmemory-visibility

Java ConcurrentHashMap.computeIfPresent value modification visibility


Let's say I have a concurrent map with collections as value:

Map<Integer, List<Integer> map = new ConcurrentHashMap<>();
map.putIfAbsent(8, new ArrayList<>());

and I update the value as follows:

map.computeIfPresent(8, (i, c) -> {
    c.add(5);
    return c;
});

I know that computeIfPresent entire method invocation is performed atomically. However, considering this map is accessed by multiple threads concurrently, I'm a little bit concerned about data visibility of the modifications done to the underlying collection. In this case will the value 5 be seen in the list after calling map.get

My question is will change to the list be visible in other threads upon calling map.get if changes are performed within computeIfPresent method call.

Please note that I am aware that changes to the list will not be visible if I were to take reference to the list before doing the update operation. I am unsure if the changes to the list will be visible if I take reference to the list (by calling map.get) after the update operation.

I am unsure how to interpret the docs, but it seems to me that happens-before relationship will guarantee visibility of the changes to the underlying collection in this particular case

More formally, an update operation for a given key bears a happens-before relation with any (non-null) retrieval for that key reporting the updated value


Solution

  • To clarify your question:

    You're providing some external guarantee such that Map.computeIfPresent() is called before Map.get().

    You haven't stated how you're doing this, but let's say you're doing it by using something with happens-before semantics provided by the JVM. If this is the case then that alone guarantees List.add() is visible to the thread calling Map.get() simply by association of the happens-before relationship.

    Now to answer the question you're actually asking: As you've noted, there's a happens-before relation between update operation ConcurrentHashMap.computeIfPresent() and the subsequent calling of the access method ConcurrentMap.get(). And naturally, there's a happens-before relation between List.add() and the end of ConcurrentHashMap.computeIfPresent().

    Put together, answer is yes.

    There's a guarantee the other thread will see 5 in the List obtained through Map.get(), provided you're guaranteeing Map.get() is actually called after computeIfPresent() ends (as stated in the question). If the latter guarantee goes foul and Map.get() is somehow called before computeIfPresent() ends, there's no guarantees to what the other thread will see since ArrayList is not thread-safe.