Memra

Thread safety: synchronized, volatile & atomic

Race conditions, the monitor lock, visibility, and lock-free counters.

The race condition problem

When two threads read-modify-write the same variable without coordination, the result is unpredictable:

// BROKEN — two threads calling increment() can both read 0, both add 1, both write 1
class Counter {
    int count = 0;
    void increment() { count++; }  // NOT atomic: read + add + write = 3 separate operations
}

synchronized methods and blocks use a monitor lock (every object has one) to ensure at most one thread executes at a time:

class Counter {
    int count = 0;
    synchronized void increment() { count++; }  // method lock on `this`

    void decrement() {
        synchronized (this) { count--; }  // explicit block — same effect
    }
}

When a thread enters a synchronized method, other threads calling any synchronized method on the same object must wait.

volatile guarantees visibility: a write to a volatile variable is immediately visible to other threads (no CPU cache hiding). It does not make compound operations like count++ atomic:

volatile boolean running = true;  // one thread writes, another reads — safe

java.util.concurrent.atomic provides lock-free atomic types:

AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet();   // atomic: read + increment + write, uninterruptible
counter.compareAndSet(5, 10); // CAS: sets to 10 only if current value is 5

AtomicInteger, AtomicLong, AtomicBoolean, AtomicReference are the main types. Prefer them over synchronized for simple counters — they are faster and have no blocking.

NORMAL ~/memra/learn/java-from-zero/thread-safety-synchronized-volatile-atomic utf-8 LF