Understanding Ordering Issues and Volatile in Java Concurrency
This article explains why intuition fails in multithreaded Java programs, demonstrates ordering problems with simple thread examples, shows how instruction reordering and JIT optimizations can produce unexpected results, and presents the volatile keyword and jcstress testing as reliable solutions to ensure correct visibility and ordering.
Concurrent programming is notoriously difficult because developers must reason about both the optimal concurrency model (e.g., single‑threaded Redis, multi‑process Nginx, or Netty’s Reactor) and low‑level memory‑visibility issues such as atomicity, visibility, and ordering, which often invalidate intuition.
The article starts with a simple two‑thread example where thread T1 sets data = 666 and then isReady = true , while thread T2 spins on while (!isReady) {} before reading data . Theoretical reasoning suggests T2 should always see data = 666 and compute r = 888 , but real executions can produce r = 222 due to instruction reordering.
Running the program repeatedly on a standard JVM shows only the expected result, yet hidden reordering can still occur because the compiler and CPU are free to reorder writes for performance. The article illustrates this with a diagram where the write to isReady may be observed before the write to data , breaking the assumed order.
To expose the problem, the author uses the OpenJDK jcstress framework. A test class annotated with @JCStressTest defines two actors: one writes data = 666 then isReady = true , the other either checks isReady with an if or a while loop and records the result. The outcomes 888 (expected) and 0 (acceptable) appear, while occasional 222 reveals the reordering bug.
The article explains why the while (!isReady) {} loop can become an infinite spin: the JIT may cache the value of isReady after the first read, never re‑loading it from memory, so the loop never observes the write.
The fix is to declare isReady as volatile . The volatile keyword prevents both compiler and CPU from reordering the write to data after the write to isReady , and it forces each read to fetch the latest value from main memory, eliminating visibility problems.
Finally, the article summarizes that writing correct concurrent Java code requires understanding these ordering pitfalls, using tools like jcstress for systematic testing, and applying volatile (or higher‑level constructs) to enforce the intended execution order.
High Availability Architecture
Official account for High Availability Architecture.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.