Hard java java-21

How does ConcurrentHashMap work internally?

The 30 second answer

ConcurrentHashMap lets many threads read and write without locking the whole map. Reads usually don't block, and writes only lock a small part of the table, so writers on different keys rarely wait for each other. Methods like compute, computeIfAbsent and merge are atomic per key. Size is only an estimate during updates, iterators never throw concurrent modification errors, and null keys or values aren't allowed.

What the Javadoc guarantees:

  • Retrievals generally don't block and reflect the most recently completed updates.
  • compute, computeIfAbsent, computeIfPresent and merge run atomically per key. Keep those functions short and never update other keys of the same map inside them.
  • size(), isEmpty() and containsValue() are only reliable when the map isn't being updated; mappingCount() returns a long.
  • Iterators are weakly consistent: they never throw ConcurrentModificationException and may or may not show updates made during iteration.
  • Null keys and values are rejected, so get returning null always means "absent".
ConcurrentHashMap<String, LongAdder> counts = new ConcurrentHashMap<>();
counts.computeIfAbsent(word, k -> new LongAdder()).increment();   // safe

if (!map.containsKey(k)) map.put(k, v);   // NOT atomic: check-then-act race
map.putIfAbsent(k, v);                    // atomic

[Impl] Since Java 8 the OpenJDK implementation no longer uses Java 7's segments: it uses CAS to insert into an empty bin, locks only the bin's first node for other updates, turns long bins into balanced trees, and lets several threads help with resizing. These details come from source-code comments, not the public spec, and can change.

Sources: ConcurrentHashMap Javadoc (Java 21), ConcurrentMap Javadoc

Sources (0)
See inline citations in the answer above.