Java volatile and happens-before: Visibility, Ordering, and Atomicity Boundaries
Baseline: Java 17+. This article discusses the Java Memory Model without relying on a specific CPU architecture.
1. What volatile guarantees
A write to a volatile variable establishes a happens-before relationship with a subsequent read of the same variable by another thread. It provides visibility and some ordering guarantees, but it does not make a group of operations atomic.
volatile boolean stopped can be used to publish a stop flag. volatile int count cannot safely support count++ because increment involves three actions: read, increment, and write-back.
2. Choosing the Right Tool
Use AtomicInteger or AtomicReference for single-variable atomic updates. Use locks, concurrent containers, or CAS loops for compound state transitions. Use volatile references to publish immutable configurations. Only consider VarHandle when low-level memory ordering is needed.
3. Sources of happens-before
Thread start, thread end, lock release and acquire, Future completion, and specified operations in concurrent containers also establish visibility relationships. Don't use "no instruction reordering" as a substitute for JMM rules.
4. Verification
Use jcstress for complex memory ordering, JMH for performance comparison, and concurrent testing with race conditions and failure injection for business state writes. volatile is not a lock, nor does it guarantee atomicity for compound operations.