The full picture
- Reads: no locking at all — volatile semantics on the table plus immutable-ish nodes make gets wait-free in the common case.
- Writes: CAS the bucket head for empty bins; otherwise lock only that bin. Resizing is cooperative — multiple threads help transfer bins.
- Compound atomicity:
computeIfAbsent(k, f)is atomic per key — but the mapping function must be short and must not touch other keys (risk of deadlock/livelock). - Iteration: weakly consistent — reflects some state since creation, never throws CME; contrast with
synchronizedMap, where you must manually synchronize the whole iteration. - No null keys/values — by design: a null get() would be ambiguous (absent vs mapped-to-null) without locking to check.