Is ConcurrentHashMap totally safe?

user697911 picture user697911 · Feb 19, 2013 · Viewed 51.6k times · Source

this is a passage from JavaDoc regarding ConcurrentHashMap. It says retrieval operations generally do not block, so may overlap with update operations. Does this mean the get() method is not thread safe?

"However, even though all operations are thread-safe, retrieval operations do not entail locking, and there is not any support for locking the entire table in a way that prevents all access. This class is fully interoperable with Hashtable in programs that rely on its thread safety but not on its synchronization details.

Retrieval operations (including get) generally do not block, so may overlap with update operations (including put and remove). Retrievals reflect the results of the most recently completed update operations holding upon their onset."

Answer

gd1 picture gd1 · Feb 19, 2013

The get() method is thread-safe, and the other users gave you useful answers regarding this particular issue.

However, although ConcurrentHashMap is a thread-safe drop-in replacement for HashMap, it is important to realize that if you are doing multiple operations you may have to change your code significantly. For example, take this code:

if (!map.containsKey(key)) 
   return map.put(key, value);
else
   return map.get(key);

In a multi-thread environment, this is a race condition. You have to use the ConcurrentHashMap.putIfAbsent(K key, V value) and pay attention to the return value, which tells you if the put operation was successful or not. Read the docs for more details.


Answering to a comment that asks for clarification on why this is a race condition.

Imagine there are two threads A, B that are going to put two different values in the map, v1 and v2 respectively, having the same key. The key is initially not present in the map. They interleave in this way:

  • Thread A calls containsKey and finds out that the key is not present, but is immediately suspended.
  • Thread B calls containsKey and finds out that the key is not present, and has the time to insert its value v2.
  • Thread A resumes and inserts v1, "peacefully" overwriting (since put is threadsafe) the value inserted by thread B.

Now thread B "thinks" it has successfully inserted its very own value v2, but the map contains v1. This is really a disaster because thread B may call v2.updateSomething() and will "think" that the consumers of the map (e.g. other threads) have access to that object and will see that maybe important update ("like: this visitor IP address is trying to perform a DOS, refuse all the requests from now on"). Instead, the object will be soon garbage collected and lost.