ConcurrentHashMap Compound Operations — Interview Questions
Reviewed by Gurusankar M.
Short Answer
ConcurrentHashMap guarantees each individual method call (get, put, remove) is thread-safe — but a get() followed by a put() based on that result is two separate operations with a window between them where another thread can interleave, the same check-then-act race a plain HashMap has, just with a thread-safe map underneath it. computeIfAbsent(), compute(), and merge() close that window because the whole read-modify-write happens atomically under the map's internal per-bin lock, as a single operation.
On this page
Concept Made Simple
Coffee Chat Question
“ConcurrentHashMap is thread-safe — so why can a get-then-put sequence still race, and how do computeIfAbsent/merge fix it?”
Remember It Faster
Mind Map Answer
'Thread-safe' on a ConcurrentHashMap means each *individual* method call is atomic and safely published across threads — it says nothing about a sequence of calls you compose yourself. if (!map.containsKey(k)) map.put(k, v) is exactly as racy as it would be on a plain HashMap; the map's internal safety doesn't extend across your own multi-statement logic.
- get() then put()
- Two separate atomic ops — a race window exists between them
- computeIfAbsent()
- Atomic: check-and-insert as ONE operation, no window
- merge()
- Atomic: combine-or-insert as ONE operation
- Caveat
- The mapping function itself should be fast — it runs while holding a lock on that bin
Key takeaway: thread-safety is per-call, not per-transaction — any time your logic spans more than one map call, reach for the compound methods instead of composing get/put yourself.
Learn by Doing
Hands-on Keyboard
ConcurrentHashMap<String, List<String>> index = new ConcurrentHashMap<>();
// RACY: two threads can both see containsKey()==false and both create a new list
if (!index.containsKey(key)) {
index.put(key, new ArrayList<>());
}
index.get(key).add(value); // also unsafe if the List itself isn't thread-safe
// CORRECT: atomic check-and-insert, single operation
index.computeIfAbsent(key, k -> new CopyOnWriteArrayList<>()).add(value);Think Beyond the Expected
What If?
What happens if the mapping function passed to computeIfAbsent() is slow or tries to modify the same map?
It runs while the map holds a lock on that key's bin, so a slow function blocks other threads touching keys in the same bin. Modifying the same map from inside its own mapping function is not a safe, supported pattern — the JDK documents this as unsupported, and doesn't guarantee what happens: depending on the exact operation it can manifest as blocked/hung threads, an exception, or other incorrect behavior. The rule to follow isn't 'expect exception X' — it's simply: never touch the same map from within its own mapping function.
Real World
This is the classic 'we used ConcurrentHashMap so it must be safe' bug — a caching layer built on get()-then-put() under load creates duplicate cache entries or, worse, wraps the value in a non-thread-safe collection (a plain ArrayList) that then corrupts under concurrent add() calls even though the outer map was never the problem.
Real Talk from Guru
In an interview I'd say: swapping HashMap for ConcurrentHashMap fixes the map's internal safety, but it doesn't automatically fix your algorithm — if your logic reads then writes based on that read, you need computeIfAbsent/merge/compute, full stop.
Interviewer's Expectation
Keywords they're listening for:
- Distinguishes per-method thread-safety from multi-step compound safety
- Can name and use computeIfAbsent/merge/compute correctly
- Knows the mapping function runs under a per-bin lock
- Warns against slow or recursive mapping functions
Common Mistakes
- Composing get()/containsKey() and put() as if the sequence were atomic
- Storing a non-thread-safe collection as the map's value
- Writing slow or map-mutating logic inside a mapping function
Best Practices
- Use computeIfAbsent/compute/merge for any read-modify-write on a shared map
- Keep mapping functions fast and free of side effects on the same map
- Pair with a thread-safe value type (CopyOnWriteArrayList, another ConcurrentHashMap) when needed
Follow-up Questions
- What's the difference between compute() and computeIfAbsent()?
- Why must the value type stored (e.g. a List) also be thread-safe on its own?
- What happens if two threads call computeIfAbsent() with the same key simultaneously?
Continue Learning with AI
Take this question deeper with your favourite AI assistant. Pick a depth, copy the prompt, or open it directly — AI is your learning companion, not a shortcut.
Plain-language foundations
I'm preparing for a software engineering interview and want to understand this from scratch, as a beginner. Topic: ConcurrentHashMap (Java Collections) Interview question: "ConcurrentHashMap is thread-safe — so why can a get-then-put sequence still race, and how do computeIfAbsent/merge fix it?" Please: 1. Explain the core idea in simple, plain language, using an everyday analogy. 2. Define any technical terms you use. 3. Walk through one small, concrete example. 4. Finish with a single sentence I can easily remember. Keep the tone friendly and assume I'm new to this topic.
Was this answer helpful?
Affiliate links
Tools for Your Developer Workspace
Equipment for a comfortable, productive setup.
As an Amazon Associate I earn from qualifying purchases.
- MonitorView on Amazon : BenQ EW2790U 27-inch Monitor (opens in a new tab)
BenQ EW2790U 27-inch Monitor
- LaptopView on Amazon : Acer Aspire One Laptop (opens in a new tab)
Acer Aspire One Laptop
- Office ChairView on Amazon : Wakefit Gravita Ergonomic Office Chair (opens in a new tab)
Wakefit Gravita Ergonomic Office Chair