Fundamentals 10 min read

Why ReentrantLock Guarantees Memory Visibility: JMM, Happens‑Before, and Memory Barriers

This article explains how Java's ReentrantLock guarantees memory visibility for shared variables without using volatile, by leveraging the Java Memory Model's happens‑before guarantees, the lock's internal volatile state, and underlying CPU memory‑barrier instructions such as the LOCK prefix.

Qunar Tech Salon
Qunar Tech Salon
Qunar Tech Salon
Why ReentrantLock Guarantees Memory Visibility: JMM, Happens‑Before, and Memory Barriers

The author, a backend engineer working on hotel pricing cache, noticed that a simple ReentrantLock test increments a shared variable count correctly even though count is not declared volatile . This raises the question: how is the updated value made immediately visible to other threads?

To answer this, the article introduces several key concepts:

Java Memory Model (JMM) : defines the semantics of how threads interact through memory, including the ordering of reads and writes to shared variables.

Memory ordering : early CPUs followed program order, but modern processors may reorder instructions unless synchronization constructs (e.g., synchronized , volatile , CAS) are used.

Happens‑before : a set of ordering rules that guarantee visibility. For example, an unlock on a monitor happens‑before every subsequent lock on the same monitor.

Although volatile is the most familiar way to obtain visibility, the article shows that the same effect is achieved by ReentrantLock because its implementation relies on a volatile field and the JMM's happens‑before guarantees.

Key code fragment of a ReentrantLock‑protected increment:

private static ReentrantLock LOCK = new ReentrantLock();
private static int count = 0;

// thread code
LOCK.lock();
try {
    count++;
} finally {
    LOCK.unlock();
}

When a thread releases the lock, the JMM defines that the unlock happens‑before the next lock acquisition, ensuring that all writes performed inside the critical section (including the increment of count ) become visible to the next thread.

The internal implementation of ReentrantLock (simplified) is also shown:

private volatile int state; // key volatile variable

protected final boolean tryAcquire(int acquires) {
    Thread current = Thread.currentThread();
    int c = getState(); // read volatile state
    // ... lock acquisition logic ...
}

protected final boolean tryRelease(int releases) {
    // ... release logic ...
    setState(c); // write volatile state (publish writes)
    return free;
}

Thus each lock acquisition performs three actions: read the volatile state, execute the critical section (e.g., count++ ), and write the volatile state on release, which publishes all prior writes.

At the hardware level, the volatile write is implemented using the x86 LOCK prefix, a memory‑barrier instruction that forces the CPU to flush pending writes to memory and ensures cache coherence across cores. This barrier provides the same visibility guarantees as a volatile write.

Consequently, the visibility of count after a ReentrantLock‑protected increment is achieved through the chain:

Lock/unlock establishes a happens‑before relationship.

The lock’s internal volatile state enforces a memory barrier (STORE‑LOAD) via the LOCK prefix.

The CPU’s cache‑coherency mechanism propagates the updated value to other cores.

The article also notes that non‑fair ReentrantLock adds an extra CAS step, which on x86 also uses the LOCK prefix and therefore provides the same memory‑barrier effect.

Summary : ReentrantLock ensures memory visibility without explicit volatile by relying on the JMM’s happens‑before rule, an internal volatile field, and the CPU’s LOCK‑prefix memory barrier that forces cache flushes and maintains coherence.

References:

http://gee.cs.oswego.edu/dl/jmm/cookbook.html

https://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-vol-3a-part-1-manual.html (Chapter 8)

https://www.ibm.com/developerworks/library/j-jtp03304/index.html

https://stackoverflow.com/questions/2972389/why-is-compareandswap-instruction-considered-expensive

http://ifeve.com/java-memory-model-5/

https://en.wikipedia.org/wiki/Memory_barrier

https://en.wikipedia.org/wiki/Java_memory_model

JavaConcurrencyMemory ModelReentrantLockHappens-Beforememory-barrier
Qunar Tech Salon
Written by

Qunar Tech Salon

Qunar Tech Salon is a learning and exchange platform for Qunar engineers and industry peers. We share cutting-edge technology trends and topics, providing a free platform for mid-to-senior technical professionals to exchange and learn.

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.