Fundamentals 21 min read

Understanding Java Memory Model, Volatile, Atomicity, Visibility, and Ordering

This article explains the Java memory model, how variables are stored in main and working memory, and why concurrency issues like dirty reads, non‑atomic operations, and instruction reordering occur, while detailing the roles of volatile, synchronized, locks, and atomic classes in ensuring visibility, ordering, and atomicity.

Architect
Architect
Architect
Understanding Java Memory Model, Volatile, Atomicity, Visibility, and Ordering

The Java Memory Model (JMM) specifies that all variables reside in main memory and each thread has its own working memory, requiring reads and writes to go through these caches, which can cause visibility and ordering problems in multithreaded programs.

Examples illustrate dirty reads, non‑atomic increments, and instruction reordering, showing why the volatile keyword alone cannot guarantee atomicity.

Three core concepts of concurrent programming—atomicity, visibility, and ordering—are defined, with Java guaranteeing atomicity only for simple reads/writes of primitive types, while synchronized , Lock , and atomic classes provide stronger guarantees.

The volatile keyword ensures that writes are immediately visible to other threads and prevents certain reordering, but it does not make compound actions atomic; proper use cases and limitations are discussed.

Implementation details of volatile are described, including memory barriers, cache invalidation, and how the JVM emits lock‑prefix instructions to enforce ordering.

Typical scenarios such as status flags, thread termination, and double‑checked locking in singleton patterns are presented, with code examples wrapped in ... blocks.

Code snippets demonstrate the problems and solutions:

i = 10++;
x = 10; // statement 1
 y = x; // statement 2
 x++; // statement 3
 x = x + 1; // statement 4
i = 9;
// Thread 1
int i = 0;
while(!stop){
    doSomething();
}
// Thread 2
stop = true;
// Example with volatile flag
volatile boolean flag = false;
while(!flag){
    doSomething();
}
// In another thread
flag = true;
class Singleton {
    private volatile static Singleton instance = null;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
public class Test {
    public volatile int inc = 0;
    public void increase() { inc++; }
    public static void main(String[] args) {
        final Test test = new Test();
        for (int i = 0; i < 10; i++) {
            new Thread(){
                public void run() {
                    for (int j = 0; j < 1000; j++) test.increase();
                }
            }.start();
        }
        while (Thread.activeCount() > 1) Thread.yield();
        System.out.println(test.inc);
    }
}
JavaconcurrencyorderingvolatileMemory ModelAtomicityVisibility
Architect
Written by

Architect

Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.

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.