ConcurrentHashMap Deep Dive: Understanding Java’s Core Concurrent Container

This article explains why HashMap isn’t thread‑safe, how Hashtable’s single lock hurts performance, and walks through the evolution from JDK 1.7 segment locks to JDK 1.8’s CAS‑plus‑synchronized bucket locking, including lock‑free size counting and a comparison with CopyOnWriteArrayList for read‑heavy scenarios.

Dabaoshi
Dabaoshi
Dabaoshi
ConcurrentHashMap Deep Dive: Understanding Java’s Core Concurrent Container

After covering locks and thread pools, this piece examines the concurrent container that almost every Java backend project relies on— ConcurrentHashMap. It appears in local caches, counters, sliding‑window rate limiters, and even thread‑pool task tracking.

HashMap vs. Hashtable

HashMap

is not thread‑safe; concurrent put can corrupt data, and during resize in JDK 7 a linked‑list may form a cycle, causing get to loop forever and CPU to spike. JDK 8 changed the resize algorithm to tail‑insertion, avoiding cycles but still not thread‑safe. Hashtable guarantees safety by wrapping every method with a single synchronized lock, effectively a global lock that serialises all accesses, turning concurrency into a bottleneck. ConcurrentHashMap aims to provide thread safety without the global lock, using two different designs in JDK 1.7 and JDK 1.8.

JDK 1.7: Segment Locks

The map maintains an array of Segment objects (default length 16, controlled by concurrencyLevel). Each Segment is a mini‑ Hashtable with its own ReentrantLock and a HashEntry array.

During put(key, value) the key is hashed to locate a specific Segment, then that segment’s lock is acquired and the entry is inserted into its internal hash table. The other 15 segments remain unlocked, allowing up to 16 concurrent writes when keys fall into different segments.

However, segment‑level locking has drawbacks: operations spanning multiple segments (e.g., size()) become complex, and the segment array size is fixed at creation, limiting scalability.

JDK 1.8: CAS + Synchronized Bucket Locking

JDK 1.8 discards Segment and reuses the classic HashMap bucket array ( Node[] table). Lock granularity is refined to a single bucket:

If a bucket is empty, a CAS operation inserts the new node without any lock.

If a bucket already contains nodes (hash collision), synchronized locks only the bucket’s head node, allowing other buckets to be accessed concurrently.

The implementation includes a simplified putVal core logic (shown below) that demonstrates the CAS‑first, fallback‑to‑synchronized approach.

for (Node<K,V>[] tab = table;;) {
    Node<K,V> f; int n, i;
    if (tab == null || (n = tab.length) == 0)
        tab = initTable(); // initialize table
    else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
        if (casTabAt(tab, i, null, new Node<>(hash, key, value)))
            break; // empty bucket, CAS succeeded
    } else if (onlyIfAbsent && f.hash == hash && ...)
        return f.val;
    else {
        synchronized (f) { // bucket head locked
            // insert or update in list/tree
        }
    }
}

The shift from ReentrantLock to optimized synchronized (post‑JDK 6) yields comparable performance under low contention and eliminates manual unlock calls.

Lock‑Free Size Counting

In JDK 1.7, size() locks every segment and aggregates counts, effectively pausing concurrency. JDK 1.8 adopts a LongAdder -like strategy: a baseCount field plus a CounterCell[] array. Updates use CAS on baseCount; under high contention, increments are spread across cells, reducing contention. The returned size is an approximate snapshot, similar to LongAdder.sum(), trading exactness for throughput.

CopyOnWriteArrayList: A Different Philosophy

For scenarios with many reads and few writes (e.g., product white‑lists in flash‑sale systems), CopyOnWriteArrayList offers an alternative: writes acquire a ReentrantLock, copy the entire underlying array, modify it, then atomically replace the reference. Reads never lock and always see a consistent snapshot of the array at the moment the iterator was created.

This design yields zero‑cost reads but incurs high write overhead proportional to the list size and write frequency. Its iterator provides weak consistency: it never throws ConcurrentModificationException but may return stale data.

Choosing the Right Container

High‑concurrency read/write Map (cache, counters): ConcurrentHashMap – CAS + bucket‑level synchronized gives near‑lock‑free reads and fine‑grained writes.

Need a strong, consistent snapshot: Use a locked HashMap or accept the approximate nature of ConcurrentHashMap.size() and traversal.

Read‑heavy, write‑rare List (whitelists, configs): CopyOnWriteArrayList – reads are lock‑free; writes copy the array.

Write‑heavy List: Prefer a locked ArrayList or Collections.synchronizedList; CopyOnWriteArrayList would be too costly.

Just need a thread‑safe tag: Avoid legacy Hashtable or Vector – their global lock hurts performance.

The overarching theme is that concurrent containers have evolved by reducing lock granularity: from a single global lock in Hashtable, to segment locks in JDK 1.7, to bucket‑level CAS‑plus‑synchronized in JDK 1.8, while CopyOnWriteArrayList takes a completely different “space‑for‑time” approach.

Next up: blocking queues and the producer‑consumer model, covering BlockingQueue families and a brief look at the Disruptor design.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaconcurrencyConcurrentHashMapJDK8CopyOnWriteArrayListLockFreeSegmentLock
Dabaoshi
Written by

Dabaoshi

Practical utilities

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.