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.