Fundamentals 14 min read

Understanding Strong vs. Weak Consistency in Java's ConcurrentHashMap

This article explains the concepts of strong and weak consistency, their roots in visibility and ordering within the Java Memory Model, and how Java 8’s ConcurrentHashMap uses volatile fields, CAS, and unsafe operations to achieve strong consistency for put/get while retaining weak consistency for operations like size().

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Understanding Strong vs. Weak Consistency in Java's ConcurrentHashMap

Last week the author saw a discussion in a GeekTime course about whether ConcurrentHashMap (CHM) provides strong or weak consistency. The article first clarifies the definitions of strong consistency (also called atomic or linearizable consistency) and weak consistency (including eventual consistency).

Strong consistency requires that every read sees the most recent write and that all processes observe operations in the same global order. Weak consistency allows a read to see stale or partial data shortly after a write.

The root causes of weak consistency are visibility and ordering problems in the Java Memory Model (JMM). The JMM separates thread‑local memory (program counter, Java stack, native stack) from shared memory (heap and method area). Because CPUs cache data, a write may stay in a core’s cache and not be flushed to main memory, causing other threads to read stale values.

Example:

// Thread 1
int i = 0;
i = 10;

// Thread 2
j = i; // may read 0 because i=10 is still in CPU cache

To guarantee visibility, Java provides volatile , synchronized , and explicit locks, which establish a happens‑before relationship ensuring that writes are flushed to memory before subsequent reads.

Instruction reordering can also break consistency. The following code may be reordered by the compiler or CPU:

int x = 1; //①
boolean flag = true; //②
int y = x + 1; //③

If reordered to ①③② , another thread may observe unexpected values. Using volatile or memory barriers prevents such reordering.

In Java 8, CHM’s internal structure is:

public class ConcurrentHashMap
extends AbstractMap
implements ConcurrentMap
, Serializable {
    transient volatile Node
[] table;
    ...
    static class Node
implements Map.Entry
{
        final int hash;
        final K key;
        volatile V val;
        volatile Node
next;
        ...
    }
}

Before Java 8, put acquired a lock and directly assigned a new Node to the table slot, while get was lock‑free and read the volatile field, leading to weak consistency for get because the write might not be visible.

V put(K key, int hash, V value, boolean onlyIfAbsent) {
    lock();
    ...
    tab[index] = new HashEntry<>(...);
    unlock();
}

V get(Object key, int hash) {
    if (count != 0) {
        Node
e = getFirst(hash);
        while (e != null) {
            if (e.hash == hash && key.equals(e.key)) {
                V v = e.value;
                if (v != null) return v;
                return readValueUnderLock(e);
            }
            e = e.next;
        }
    }
    return null;
}

Java 8 replaces the direct array assignment with a CAS operation and reads with Unsafe.getObjectVolatile , achieving strong consistency for put and get :

private static final sun.misc.Unsafe U;

final V putVal(K key, V value, boolean onlyIfAbsent) {
    for (Node
[] tab = table;;) {
        if ((f = tabAt(tab, i = (n-1) & hash)) == null) {
            if (casTabAt(tab, i, null, new Node<>(hash, key, value, null)))
                break;
        }
    }
    ...
}

static final
boolean casTabAt(Node
[] tab, int i, Node
c, Node
v) {
    return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}

static final
Node
tabAt(Node
[] tab, int i) {
    return (Node
)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
}

Because both reads and writes now operate directly on volatile memory locations, the happens‑before guarantees of volatile , synchronized , and locks ensure that a get sees the most recent put . However, not all CHM operations are strongly consistent; for example, size() remains weakly consistent in Java 8.

In summary, Java 8’s redesign of ConcurrentHashMap—removing segment locks, using per‑slot CAS, and leveraging unsafe volatile reads—provides strong consistency for the core put / get path while preserving high concurrency, and developers should still be aware of the consistency guarantees of each method.

JavaConcurrencyConcurrentHashMapMemory Modelconsistency
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

login 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.