10. Multithreading & JMM

Java Multithreading, Memory Model (JMM) & Lock Concurrency

Deep technical guide to Java Multithreading, Thread Lifecycle, Java Memory Model (JMM), volatile visibility, synchronized intrinsic locks, ReentrantLock, and Atomic CAS operations.

🚦 Java Thread Lifecycle & Execution

A Thread is the smallest unit of concurrent execution managed by the operating system kernel.

  • Analogy: Multi-Lane Highway & Dual-Custody Bank Vault Locks.
    • Single Threading: A single-lane road with 1 car driving at a time.
    • Multithreading: A 4-lane highway with 4 cars driving side-by-side concurrently.
    • volatile: An emergency broadcast banner displayed across all lane monitors simultaneously, ensuring every driver sees updated weather conditions instantly without relying on old cached dashboard mirrors.
    • synchronized: A dual-custody bank vault lock. Only 1 security guard carrying the key card (Monitor Lock) can enter the vault at a time; all other guards must wait in line (BLOCKED).
NEW Thread Created RUNNABLE Ready / Running BLOCKED Waiting for Lock WAITING Object.wait TIMED_WAITING Thread.sleep TERMINATED Execution Complete

🧠 Java Memory Model (JMM) & volatile Visibility

The Java Memory Model (JMM) defines the specification for how threads interact through shared main RAM memory vs private CPU L1/L2 caches.

                           Java Memory Model (JMM)

    Thread 1 (CPU Core 1)                        Thread 2 (CPU Core 2)
  ┌───────────────────────┐                    ┌───────────────────────┐
  │ Local Cache (x = 5)   │                    │ Local Cache (x = 5)   │
  └───────────┬───────────┘                    └───────────┬───────────┘
              │                                            │
              └───────────────► Main RAM ◄─────────────────┘
                              (Shared x = 10)

The volatile Keyword: Memory Visibility & Ordering

When a field is declared volatile:

  1. Visibility Guarantee: Flushes writes directly to shared Main RAM memory immediately, bypassing CPU L1/L2 caches, guaranteeing that subsequent reads by other threads see the updated value instantly.
  2. Happens-Before Relationship: Prevents CPU instruction reordering across the memory barrier.

[!CAUTION] volatile is NOT Atomic: volatile guarantees visibility, but it does NOT guarantee atomicity! Executing volatileCount++ is still a 3-step non-atomic read-modify-write operation prone to race conditions. Use AtomicInteger for atomic increments!


🔒 Intrinsic Locks (synchronized) vs. ReentrantLock

MetricIntrinsic Lock (synchronized)Explicit Lock (ReentrantLock)
Lock AcquisitionAutomatic block-scoped acquisition & releaseManual lock.lock() and lock.unlock() in finally
InterruptibilityCannot interrupt a thread waiting for lockSupports lockInterruptibly()
Fairness PolicyUnfair lock acquisitionSupports Fair Lock queue (new ReentrantLock(true))
Try LockNot supportedSupports tryLock(timeout, timeUnit)
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockDemo {
    private final ReentrantLock lock = new ReentrantLock();
    private int counter = 0;

    public void increment() {
        lock.lock(); // Explicit Lock Acquisition
        try {
            counter++;
        } finally {
            lock.unlock(); // Always release in finally block!
        }
    }
}

⚛️ Atomic Classes & CAS Operations (AtomicInteger)

Atomic classes (AtomicInteger, AtomicReference) achieve lock-free thread safety using CPU hardware-level CAS (Compare-And-Swap) instructions:

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicCounterDemo {
    // Lock-free thread-safe atomic counter
    private final AtomicInteger count = new AtomicInteger(0);

    public void increment() {
        // Uses hardware-level CAS loop: compareAndSet(expected, update)
        count.incrementAndGet();
    }

    public int getCount() {
        return count.get();
    }
}

❓ Conceptual Quizzes

Knowledge Check

What memory guarantee does the volatile keyword provide in Java?

Knowledge Check

Why is it mandatory to call ReentrantLock.unlock() inside a finally block?


💻 Practice Problems

Problem: Lock-Free Thread-Safe Counter Benchmark

Write a program where 10 concurrent threads each increment a shared AtomicInteger counter 1,000 times using an ExecutorService, and print the final counter value (expecting 10,000).

On this page