Skip to main content
๐Ÿ“œ WAYPOINT LESSON

Races & Visibility

โญโญ intermediateโณ 15 min read๐Ÿ“ Lesson 95 of 180

Lost updates, invisible writes, and the volatile โ†’ atomic โ†’ synchronized ladder with when each is correct.

Race conditions and visibility

Two problems plague shared mutable state:

Interference โ€” count++ is read-modify-write; two threads can read the same value and lose an increment:

class Counter { int count; void inc() { count++; } }
// 2 threads ร— 10_000 increments โ†’ often < 20_000

Visibility โ€” without synchronization, one thread's writes may never become visible to another (the JIT may hoist the read out of a loop):

boolean stop = false;          // plain field โ€” may never be seen!
// Thread A: while (!stop) work();
// Thread B: stop = true;

Fixes, weakest to strongest:

  • volatile โ€” visibility only; fine for flags, wrong for count++
  • AtomicLong/AtomicInteger โ€” atomic read-modify-write (incrementAndGet())
  • synchronized blocks โ€” mutual exclusion for compound actions
class SafeCounter {
    private final AtomicLong count = new AtomicLong();
    void inc() { count.incrementAndGet(); }
    long value() { return count.get(); }
}

Rule of thumb: make shared state immutable, or confine it to one thread, or protect every access consistently โ€” pick one and document it.