ConcurrentHashMap Flashcards

KOTLIN › Concurrency

What does ConcurrentHashMap guarantee that HashMap doesn't?
Safe concurrent use with no external locking: reads never block, each operation is atomic and thread-safe, and there's a built-in happens-before edge: everything a thread did before putting a value is visible to any thread that later retrieves it.
How does a write work internally in the Java 8+ implementation?
Hash to a bin. If the bin is empty, CAS the new node in with no lock at all. If it's occupied, synchronize on that bin's head node only, then append/update the chain (or tree). Contention is per-bin, so writes to different buckets proceed fully in parallel.
Why can get() run without any locking?
Node value and next pointers are volatile, so a reader traversing a bin sees safely published nodes and values (a JMM visibility guarantee, not luck). Worst case it reads the value as of some very recent moment, never a torn or half-built entry.
What happens when ConcurrentHashMap resizes under load?
Resize is cooperative: threads doing writes that notice a resize in progress help transfer bins to the new table. Moved bins get a ForwardingNode so readers follow the pointer to the new table and never block. No stop-the-world rehash.
The map is thread-safe, so why is if (map[k] == null) map[k] = v still broken?
Atomicity is per operation, not per sequence: two threads can both see null and both write. Compound logic must use the atomic compound ops: putIfAbsent, compute, computeIfAbsent, merge, or replace(key, expected, new).
What are the rules for lambdas passed to compute / computeIfAbsent?
Short, fast, and no side effects on the same map: the bin is locked while your function runs, so slow work blocks that bin, and re-entrant updates (e.g. a recursive computeIfAbsent) can throw IllegalStateException or deadlock. Heavy loading belongs outside, or in a cached Deferred pattern.
How do iterators, size() and isEmpty() behave under concurrent updates?
Iterators are weakly consistent: they never throw ConcurrentModificationException, visit each element at most once, and may or may not reflect concurrent changes. size()/isEmpty() aggregate striped counters and are moving estimates: never gate correctness on them.
Why does ConcurrentHashMap forbid null keys and values?
get() returning null must mean exactly 'absent'. In a concurrent map you can't disambiguate with a follow-up containsKey(), because the answer can change between the two calls. Banning stored nulls keeps every response unambiguous; express absence logic with the atomic ops instead.
What do Kotlin's getOrPut on a ConcurrentMap, newKeySet(), and the bulk operations give you?
getOrPut is implemented with putIfAbsent: the result is consistent, but the default-value function may run more than once under a race, so it must be side-effect-free. newKeySet() is a concurrent Set view backed by CHM. forEach/search/reduce take a parallelismThreshold to fan work across threads.

Back to ConcurrentHashMap