Skip to main content

Command Palette

Search for a command to run...

Compare-And-Swap (CAS) in Java with `VarHandle`

Updated
3 min read

Concurrency is hard, but Java gives us powerful tools to make it manageable. One such tool is the Compare-And-Swap (CAS) operation, which lies at the heart of lock-free programming. In this blog post, we’ll explore how CAS works, why it needs to be used in a loop, how VarHandle fits in, and why the volatile keyword is critical.


🧠 What Is CAS?

Compare-And-Swap (CAS) is an atomic instruction used to update a variable only if it hasn't changed since the last time it was read. CAS is the building block of non-blocking algorithms.

CAS requires:

  • Memory location (V) — the variable to modify
  • Expected value (A) — what the thread believes is currently in V
  • New value (B) — the value to write

If V == A, CAS sets V = B and returns true; otherwise it returns false.


🔁 Why CAS Needs a Loop

CAS may fail if another thread modifies the variable between your read and write. That's why CAS is typically wrapped in a retry loop:

do {
    oldValue = value.get();
    newValue = oldValue + 1;
} while (!value.compareAndSet(oldValue, newValue));

This retry loop ensures eventual success even under contention.


🛠 Using VarHandle for CAS (Java 9+)

Java 9 introduced VarHandle to safely and flexibly perform atomic operations.

✅ Example: Increment with VarHandle

import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;

public class Counter {
    private volatile int value = 0; // must be volatile

    private static final VarHandle VALUE_HANDLE;

    static {
        try {
            VALUE_HANDLE = MethodHandles.lookup()
                .findVarHandle(Counter.class, "value", int.class);
        } catch (ReflectiveOperationException e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    public void increment() {
        int oldVal, newVal;
        do {
            oldVal = (int) VALUE_HANDLE.getVolatile(this);
            newVal = oldVal + 1;
        } while (!VALUE_HANDLE.compareAndSet(this, oldVal, newVal));
    }

    public int get() {
        return (int) VALUE_HANDLE.getVolatile(this);
    }
}

📌 Why the Field Must Be volatile

Using volatile ensures:

  • Visibility: All threads see the most recent value of the variable.
  • Happens-before guarantee: Writes before a volatile write are visible to threads reading that variable.

Without volatile, even though compareAndSet is atomic, other threads might see stale values, leading to incorrect results.


📊 Visual Representation of CAS With VarHandle

CAS VarHandle Diagram

In this diagram:

  • Thread 1 reads and attempts a CAS update.
  • Thread 2 reads concurrently.
  • volatile ensures visibility and proper memory ordering.
  • CAS may retry if another thread modifies the value before it completes.

🔒 Summary

  • CAS is a powerful, low-level primitive for writing lock-free concurrent algorithms.
  • It's used in Atomic* classes, Unsafe, and VarHandle.
  • VarHandle is the modern and safer alternative to Unsafe.
  • Declaring fields as volatile is essential to ensure memory visibility and correctness.

🧪 Further Reading