Fundamentals 9 min read

Deep Dive into Java Concurrency (Part 3): The Low‑Level Mechanics of volatile and CAS

This article explains Java's volatile and CAS primitives, detailing volatile's visibility and ordering guarantees, the memory barriers the JVM inserts, CAS's atomic compare‑and‑swap operation, common pitfalls like the ABA problem, and how their combination enables lock‑free thread safety, with code examples and interview tips.

Coder Trainee
Coder Trainee
Coder Trainee
Deep Dive into Java Concurrency (Part 3): The Low‑Level Mechanics of volatile and CAS

The article continues a series on Java concurrency, focusing on the lightweight primitives volatile and CAS, which form the foundation of the JUC package.

1. volatile memory semantics

It first asks what volatile guarantees and what it does not.

Visibility : writes to a volatile variable are immediately flushed to main memory; reads fetch the latest value.

Ordering : prevents instruction reordering via memory barriers.

Atomicity : volatile count++ is not atomic.

1.1 volatile happens‑before rule

Write to a volatile variable happens‑before any subsequent read of that variable.
Thread A                     Thread B
─────────────────────────────────────────────────
volatile boolean flag = true;   if (flag) { ... }
      │                         ▲
      │      happens‑before      │
      └─────────────────────────┘
All actions before Thread A writes flag become visible to Thread B.

This establishes a lightweight synchronization boundary.

1.2 volatile memory barriers

To implement the semantics, the JVM inserts memory barriers around volatile reads and writes:

StoreStore barrier before a volatile write – prevents earlier ordinary writes from being reordered after the volatile write.

StoreLoad barrier after a volatile write – the most expensive barrier; flushes the store buffer to main memory.

LoadLoad + LoadStore barriers after a volatile read – ensures later reads/writes are not reordered before the volatile read.

The StoreLoad barrier is the costliest, which explains why volatile writes are slower than ordinary writes.

2. CAS (Compare And Swap) principle

CAS is the cornerstone of the entire JUC package; all high‑level atomic classes ultimately rely on it.

2.1 What is CAS?

CAS is an atomic operation with three parameters:

V : the variable to update.

E : the expected (old) value.

N : the new value.

Semantics: the variable is updated to N only if its current value equals E; otherwise nothing happens.

CAS is implemented by a single CPU instruction (e.g., cmpxchg) and is therefore atomic and non‑interruptible.

┌─────────────────────────────────────────────────────────────────┐
│                     CAS execution flow                           │
├─────────────────────────────────────────────────────────────────┤
│ if (V == E) {          // memory value equals expected value   │
│     V = N;            // update to new value                  │
│     return true;      // success                             │
│ } else {                                                   │
│     return false;     // failure (value changed by another) │
│ }                                                          │
│ • CPU guarantees atomicity                                   │
│ • No lock, no thread blocking                               │
│ • Caller decides whether to retry on failure                │
└─────────────────────────────────────────────────────────────────┘

2.2 Java’s CAS implementation

public final int incrementAndGet() {
    return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}

// Unsafe.compareAndSwapInt signature
public final native boolean compareAndSwapInt(Object o, long offset,
                                            int expected, int x);

2.3 Three classic CAS problems

Problem 1: ABA

Scenario: a variable changes A → B → A; CAS cannot detect the intermediate change.

// Thread 1 wants to change A to C
// Thread 2 changes A to B, then B back to A
// Thread 1 sees V == E (A == A) and CAS succeeds, but the value is not the original A

Solution: use a version number or timestamp, e.g., AtomicStampedReference.

AtomicStampedReference<String> ref = new AtomicStampedReference<>("A", 0);
int stamp = ref.getStamp();
ref.compareAndSet("A", "B", stamp, stamp + 1);

Problem 2: Long spin loops

Spinning CAS that fails repeatedly consumes CPU.

while (!atomic.compareAndSet(expected, newValue)) {
    // spin until success
}

Solution: limit spin count or apply back‑off strategies.

Problem 3: Single‑variable limitation

CAS can only atomically modify one variable.

Wrap multiple values in an object and use AtomicReference.

Fall back to a lock when multiple variables must be updated together.

3. Unsafe class

All Java CAS operations eventually delegate to the Unsafe class, which provides low‑level memory access.

public final class Unsafe {
    public static Unsafe getUnsafe() { ... }
    public native boolean compareAndSwapInt(Object o, long offset,
                                            int expected, int x);
    public native boolean compareAndSwapLong(Object o, long offset,
                                             long expected, long x);
    public native boolean compareAndSwapObject(Object o, long offset,
                                                Object expected, Object x);
    public native long allocateMemory(long bytes);
    public native void freeMemory(long address);
    public native long objectFieldOffset(Field field);
}

4. volatile + CAS = lock‑free concurrency

volatile

guarantees visibility and ordering; CAS guarantees atomicity. Their combination yields lock‑free thread safety.

// Lock‑free counter
public class Counter {
    private volatile int value;
    public void increment() {
        int old, newVal;
        do {
            old = value;
            newVal = old + 1;
        } while (!compareAndSwapInt(valueOffset, old, newVal));
        // spin until success
    }
}

5. Interview‑ready answer

When asked “What is the relationship between volatile and CAS?” a concise answer can be: volatile provides visibility and ordering but not atomicity.

CAS is a CPU atomic instruction that provides atomicity.

The combination volatile + CAS achieves lock‑free thread safety.

The entire JUC package (Atomic*, AQS) is built on this combination.

6. Next preview

The next article will deep‑dive into AQS source code.

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.

JavaconcurrencyCASvolatilehappens-beforeUnsafememory barrier
Coder Trainee
Written by

Coder Trainee

Experienced in Java and Python, we share and learn together. For submissions or collaborations, DM us.

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.