I've interviewed hundreds of developers, and this question catches a lot of them because the code looks perfectly safe.
ConcurrentHashMap<String, Integer> counts = new ConcurrentHashMap<();
counts.put("A", 1);
if (counts.get("A") != null) {
counts.put("A", counts.get("A") + 1);
}
We're using ConcurrentHashMap. So this is thread-safe, right?
Not necessarily !!!
The problem
ConcurrentHashMap makes each individual operation thread-safe, get(), put(), remove(). It does not make a sequence of operations atomic.
get() and put() here are two separate steps. If two threads run this block at the same time:
Both read A = 1
Both compute 1 + 1 = 2
Both write A = 2
Expected result: 3. Actual result: 2. One update silently disappears.
The fix
Use an operation that reads and updates in one atomic step:
counts.merge("A", 1, Integer::sum);
or
counts.compute("A", (key, value) -> value == null ? 1 : value + 1);
Now the read-modify-write happens as a single atomic operation on that key.
The interview lesson
Weak answer: "It's a ConcurrentHashMap, so it's thread-safe."
Strong answer: "Individual operations are thread-safe, but a sequence of operations isn't automatically atomic."
Rule of thumb: any time you see read → calculate → write, ask if another thread could change the value in between. If yes, you likely have a race condition, no matter which "thread-safe" class you're using.
Have you run into a ConcurrentHashMap race condition like this, in production or in an interview?
Top comments (0)