# Lock-Free Programming - Java (Part 2)

Before we can write lock-free code, we need to understand a mind-bending truth:

> **The code you write is NOT the code that runs.**

Compilers reorder your instructions. CPUs execute them out of order. Caches hold stale data. And all of this is invisible to you — until you add threads. Then it becomes a minefield.

This article explains why this happens and how Java's **Memory Model** gives you tools to tame the chaos.

* * *

## Why Memory Isn't What You Think

When you write Java code, you probably imagine something like this:

```plaintext
Your Code → CPU → Memory → Done
```

The reality is far more complex:

![](https://cdn.hashnode.com/uploads/covers/637f189ed7d9bcd845996b4b/0ad5edd2-cb75-4051-aba7-69391c2a27b1.png align="center")

Each CPU core has its own **cache** (a fast, local copy of memory) and a **store buffer** (a queue of pending writes). When Thread 1 writes a value, it doesn't go to main memory immediately. It might sit in Thread 1's store buffer or cache — invisible to Thread 2.

### A Scary Example

```java
public class VisibilityBug {
    private boolean ready = false;
    private int answer = 0;

    // Thread 1 runs this
    public void writer() {
        answer = 42;       // Write 1
        ready = true;      // Write 2
    }

    // Thread 2 runs this
    public void reader() {
        if (ready) {                  // Read 1
            System.out.println(answer); // Read 2
        }
    }
}
```

**Quiz**: If Thread 2 sees `ready == true`Will it always print `42`?

**Answer**: **No!** It might print `0`. Here's why:

![](https://cdn.hashnode.com/uploads/covers/637f189ed7d9bcd845996b4b/c0216eed-764d-48c7-90dc-80516b0a1a45.png align="center")

Two things went wrong:

1.  **Write reordering**: The CPU decided it was faster to write `ready` before `answer`
    
2.  **Cache staleness**: Thread 2's cache still had the old value of `answer`
    

This is perfectly legal behavior for modern CPUs. They guarantee nothing about the order other cores see your writes — unless you tell them to.

* * *

## Three Villains of Shared Memory

### Villain 1: Compiler Reordering

The Java compiler (and the JIT compiler at runtime) can rearrange your code for optimization:

```plaintext
// What you wrote:
x = 1;
y = 2;

// What might execute:
y = 2;
x = 1;
```

This is safe in a single thread (the end result is the same). But with multiple threads reading `x` and `y`, the order matters!

### Villain 2: CPU Instruction Reordering

Even if the compiler preserves your order, the CPU itself can execute instructions out of order. Modern CPUs have deep pipelines and will rearrange instructions to keep all execution units busy.

### Villain 3: Cache Incoherence

Each core's cache can hold different versions of the same variable. Without explicit synchronization, there's no guarantee that one core sees another core's writes.

![](https://cdn.hashnode.com/uploads/covers/637f189ed7d9bcd845996b4b/d26abbdb-aa4c-4186-a135-b81f2c55feca.png align="center")

* * *

## The Java Memory Model (JMM) to the Rescue

Java doesn't leave you at the mercy of hardware quirks. The **Java Memory Model** (defined in JSR 133, part of the language specification since Java 5) provides a set of rules that guarantee when writes by one thread become visible to reads by another thread.

The key concept is the **happens-before** relationship.

### What Is Happens-Before?

**Happens-before** is a guarantee: if action A *happens-before* action B, then:

*   A's results are **visible** to B
    
*   A is **ordered** before B (no reordering can swap them)
    

Think of it as a contract between you and the JVM:

> "If you use these specific constructs, I promise your writes will be visible in the right order."

![](https://cdn.hashnode.com/uploads/covers/637f189ed7d9bcd845996b4b/65514e05-23be-4ec3-9038-bbf2c648f129.png align="center")

### The Happens-Before Rules

Here are the key rules that establish happens-before relationships:

![](https://cdn.hashnode.com/uploads/covers/637f189ed7d9bcd845996b4b/dc87f8f9-78be-42d2-9093-8ff357f076ed.png align="center")

* * *

## Volatile: Your First Memory Fence

The `volatile` keyword is the simplest way to create happens-before relationships between threads.

```java
public class VisibilityFixed {
    private volatile boolean ready = false;  // volatile!
    private int answer = 0;

    // Thread 1
    public void writer() {
        answer = 42;        // Write 1
        ready = true;       // Write 2 (volatile write)
    }

    // Thread 2
    public void reader() {
        if (ready) {                    // Read 1 (volatile read)
            System.out.println(answer); // Guaranteed to print 42!
        }
    }
}
```

### Why Does This Work?

![](https://cdn.hashnode.com/uploads/covers/637f189ed7d9bcd845996b4b/8bbb6f29-58c9-4542-81c4-e878e3ea173d.png align="center")

A **volatile write** acts like a barrier:

*   All writes **before** the volatile write are flushed to main memory
    
*   The volatile write itself is immediately visible to other threads
    

A **volatile read** acts like a refresh:

*   The CPU invalidates its cache and reads fresh values from main memory
    
*   All reads **after** the volatile read see the latest values
    

### What Volatile Does NOT Do

`volatile` ensures **visibility** and **ordering**, but NOT **atomicity** of compound operations.

```java
private volatile int count = 0;

// THIS IS STILL BROKEN!
public void increment() {
    count++;  // read + add + write — NOT atomic even with volatile!
}
```

The `count++` is three operations. `volatile` ensures each individual read and write is visible to other threads, but it **doesn't prevent** two threads from reading the same value before writing. For that, you need CAS operations.

![](https://cdn.hashnode.com/uploads/covers/637f189ed7d9bcd845996b4b/44d33209-fe08-4312-a2bd-748e24820a0c.png align="center")

* * *

## Understanding Memory Barriers (Fences)

Under the hood, `volatile` and `synchronized` work by inserting **memory barriers** (also called **fences**) into the instruction stream. A memory barrier is a CPU instruction that says: "Don't reorder anything past this point."

There are four types:

| **Barrier Type** | **What It Prevents** |
| --- | --- |
| **LoadLoad** | Ensures loads before the barrier are completed before loads after it |
| **StoreStore** | Ensures stores before the barrier are complete before stores after it |
| **LoadStore** | Ensures loads before the barrier are complete before stores after it |
| **StoreLoad** | Ensures stores before the barrier are completed before loads after it (most expensive!) |

![](https://cdn.hashnode.com/uploads/covers/637f189ed7d9bcd845996b4b/25c4e145-0f18-4109-b3da-359b0d4cc85f.png align="center")

You rarely need to think about individual fence types in Java — `volatile` and `synchronized` handle them for you. But understanding that they exist helps you appreciate the cost: **memory barriers force the CPU to give up some of its optimization tricks**, which is why they're not free.

* * *

## Practical Example: A Safe Publication Pattern

One of the most common bugs in concurrent Java is **unsafe publication** — exposing an object to another thread before it's fully constructed.

### The Bug

```java
public class Holder {
    private int value;
    
    public Holder(int n) {
        this.value = n;
    }
    
    public int getValue() {
        return value;
    }
}

// Shared between threads
public class Unsafe {
    private Holder holder;  // NOT volatile
    
    public void initialize() {
        holder = new Holder(42);
    }
    
    public void use() {
        if (holder != null) {
            // Can print 0! The other thread might see a
            // partially-constructed Holder!
            System.out.println(holder.getValue());
        }
    }
}
```

### The Fix

```java
public class Safe {
    private volatile Holder holder;  // volatile!
    
    public void initialize() {
        holder = new Holder(42);
    }
    
    public void use() {
        Holder h = holder;  // single volatile read
        if (h != null) {
            System.out.println(h.getValue());  // Always 42 ✅
        }
    }
}
```

The `volatile` write to `holder` ensures that all writes during the Holder's constructor (including `this.value = n`) are visible to any thread that reads `holder`.

![](https://cdn.hashnode.com/uploads/covers/637f189ed7d9bcd845996b4b/d15a551a-58ea-41ae-9868-9f3708e9ad6a.png align="center")

* * *

## How This Connects to Lock-Free Programming

Everything in lock-free programming depends on the Java Memory Model:

![](https://cdn.hashnode.com/uploads/covers/637f189ed7d9bcd845996b4b/4dc49d2e-4bc8-438b-a57e-b1b88fb637d6.png align="center")

The atomic classes (`AtomicInteger`, `AtomicReference`, etc.) that power lock-free code provides:

1.  **Volatile-like visibility** — writes and reads have happens-before semantics
    
2.  **CAS operations** — atomic read-modify-write in a single hardware instruction
    

Without the JMM's happens-before guarantees, none of our lock-free algorithms would be correct. The CAS operation itself creates a happens-before edge — a successful `compareAndSet` A variable guarantees that all writes before the CAS are visible to any thread that subsequently reads that variable.

* * *

## Key Takeaways

1.  **CPUs and compilers reorder operations** for performance — this is invisible in single-threaded code, but deadly in multi-threaded code
    
2.  **Each CPU core has its own cache** — writes by one core may not be immediately visible to other cores
    
3.  **The Java Memory Model** defines when writes become visible through **happens-before** relationships
    
4.  `volatile` ensures visibility and ordering, but NOT atomicity of compound operations like `count++`
    
5.  **Memory barriers** are the low-level mechanism that enforces ordering — `volatile` and `synchronized` insert them for you
    
6.  **Atomic classes** combine volatile semantics with CAS operations — this is the foundation of lock-free code
