Lab 04: Java Memory Model

Time: 60 minutes | Level: Architect | Docker: docker run -it --rm zchencow/innozverse-java:latest bash


Overview

The Java Memory Model (JMM) defines happens-before relationships that guarantee visibility across threads. Master VarHandle for fine-grained memory access, StampedLock for optimistic reads, and LongAdder for high-contention counters.


Step 1: Java Memory Model — Happens-Before Rules

JMM Happens-Before (HB) partial order:
  1. Program order within a thread
  2. Monitor lock/unlock: unlock HB → lock
  3. volatile write HB → volatile read (same variable)
  4. Thread.start() HB → first action in new thread
  5. Thread.join() HB → actions after join()
  6. Static initializer HB → first use of class
  7. Object construction HB → finalizer
// Without HB: data race — value may not be visible
int x = 0;
boolean flag = false;
// Thread 1:            // Thread 2:
x = 42;                // while (!flag) {}
flag = true;           // System.out.println(x); // may print 0!

// With volatile: HB guaranteed
volatile boolean vFlag = false;
// Thread 1:            // Thread 2:
x = 42;                // while (!vFlag) {}  // volatile read sees volatile write
vFlag = true;          // System.out.println(x); // guaranteed: 42
                       // (volatile write flushes all prior writes)

💡 volatile guarantees visibility but NOT atomicity. volatile long reads are atomic on 64-bit JVMs but not specified by JMM for 32-bit.


Step 2: VarHandle — Fine-Grained Memory Access

VarHandle (Java 9+) provides access modes for precise memory ordering — cheaper than AtomicInteger for some patterns.

VarHandle access mode hierarchy:


Step 3: StampedLock — Optimistic Reads

StampedLock (Java 8+) offers three locking modes with optimistic read being the key innovation — a read that doesn't even acquire a lock.

💡 Use StampedLock when reads vastly outnumber writes. Optimistic reads have zero overhead when no writer is active.


Step 4: LongAdder vs AtomicLong Under Contention

Internals comparison:


Step 5: False Sharing and @Contended


Step 6: Memory Barriers in Practice


Step 7: Lock-Free Queue Pattern


Step 8: Capstone — VarHandle CAS + StampedLock Demo

📸 Verified Output:


Summary

Concept
API
Use Case

Happens-before

JMM spec

Reasoning about visibility

Volatile

volatile keyword

Flag variables, single writer

VarHandle CAS

compareAndSet()

Lock-free data structures

Acquire/Release

getAcquire/setRelease

Producer/consumer, cheaper than volatile

StampedLock

tryOptimisticRead()

Read-heavy, rare writes

LongAdder

increment(), sum()

High-contention counters

False sharing

@Contended

Hot fields in different cache lines

Lock-free queue

AtomicReference CAS

Non-blocking concurrent queues

Last updated