Mastering Java’s Happens‑Before Principle and Memory Model
This article explains the Java Memory Model, how threads interact with main and working memory, and details the happens‑before rules—including program order, monitor, volatile, thread start, join, and transfer—to help developers understand visibility and ordering challenges in concurrent Java programs.
JMM Design
The Java Memory Model (JMM) underpins the JVM, defining how heap, method, and stack areas operate and how threads access memory. Each thread has its own working memory for local variables, while shared variables reside in main memory (essentially shared memory). Threads load shared variables into working memory, modify them, and store them back, which can cause visibility and ordering problems.
Because each thread works on its own copy, concurrent writes may overwrite each other, leading to unsafe shared variable states.
This design gives rise to the classic visibility and ordering issues in Java concurrency.
happens‑before Principle
JSR‑133 defines a set of happens‑before rules that establish execution order between actions, ensuring visibility across threads. The principle is primarily used to solve inter‑thread visibility problems.
Program Order Rule
Each action in a thread happens‑before every subsequent action in that same thread.
Note: This ordering applies only within a single thread.
Monitor Rule
An unlock on a monitor happens‑before every subsequent lock on that same monitor.
Example using synchronized methods demonstrates the monitor rule:
class monitorLock {
private int value = 0;
public synchronized int getValue() {
return value;
}
public synchronized void setValue(int value) {
this.value = value;
}
}When one thread executes getValue and another executes setValue, the monitor ensures that the write to value becomes visible to the reading thread. Without synchronized, visibility is not guaranteed.
The monitor rule mirrors the semantics of ReentrantLock 's lock and unlock operations.
volatile Rule
A write to a volatile field happens‑before every subsequent read of that field, introducing a memory barrier (fence) that forces ordering of instructions before and after the barrier.
Thread start Rule
Calling start() on a thread happens‑before any actions in the newly started thread.
Thus, all actions before start() are flushed to main memory, and the new thread reads them from there.
Thread join Rule
All actions in a thread happen‑before any other thread successfully returns from join() on that thread.
In the diagram, action 2 happens‑before action 4 via the join rule, and action 4 happens‑before action 5 via program order, yielding a transitive ordering from 2 to 5.
Thread transfer Rule
If action A happens‑before B and B happens‑before C, then A happens‑before C. This transitivity is used together with other rules.
For example, combining ordinary writes, volatile writes/reads, and ordinary reads shows how a plain write can be ordered before a plain read through the chain of happens‑before relations.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
ITPUB
Official ITPUB account sharing technical insights, community news, and exciting events.
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.
