Happens-Before in Java: What Every Java Developer Should Know
What is Happens-Before?
In the Java Memory Model, happens-before is a set of rules that define when the result of one thread's action is guaranteed to be visible to another thread.
If action A happens-before action B, it means:
A's changes to memory are visible to B
A is considered to occur before B in a guaranteed way
This concept becomes critical in a multi-threading environment because threads can be executed independently and can access shared data simultaneously. Without this concept, a thread might see stale data or inconsistent data, leading to unpredictable behavior and difficult to diagnose bugs
The Core Happens-Before Rules
The Java Memory Model (JMM) defines a specific set of rules. If you can connect two operations through this chain of rules, you have a happens-before relationship
Program order rule
Within a single thread, each action is executed sequentially
x = 1; y = 2;Inside the same thread, x = 1 happens-before y = 2. That is guaranteed
Monitor Lock Rule
An unlock on a monitor lock happens-before every sub-sequent lock on the same monitor lock. Results of all the actions that happened before releasing the lock are visible to the thread that acquired the lock.
## Thread 1 synchronized (data) { data.value = 42; data.ready = true; } // <-- unlock happens here ## Thread 2 synchronized (data) { // <-- lock happens here if (data.ready) { System.out.println(data.value); } } // data.ready = true and data.value = 42
Volatile Variable Rules
A write to a volatile variable happens-before every subsequent read of that variable. This means if one thread modifies a volatile variable, and another thread reads it afterward, the second thread will see the updated value.
Tranvity Rule
If A happens-before B and B happens-before C, then A happens-before C
class SharedData { int value = 0; volatile boolean ready = false; public void writer() { value = 42; // Action A ready = true; // Action B (volatile write) } public void reader() { if (ready) { // Action C (volatile read) // What is the value of value here? System.out.println(value); // Action D } } }The result is guaranteed to be 42 due to the transitivity rule of the happens-before relationship.
Thread Start Rule
A call to Thread.start() on a thread happens-before any action in the started thread.
This ensures that when a new thread starts, it can see all the variable values that were set by parent thread before it was started
Thread Join Rule
A call to Thread.join() on a thread happens-before the method returns, meaning the joined thread has completed execution and all its effects are visible.
class Example { static int result = 0; public static void main(String[] args) throws InterruptedException { Thread worker = new Thread(() -> { result = 42; // Action in worker thread }); worker.start(); worker.join(); // Wait for worker to finish System.out.println(result); // What is printed? } }Due to Thread.join rule, the result is 42
Final Fields Rule
Proper construction and publication of objects with final fields ensure that once a constructor finishes, the values of those fields are visible to all threads, provided the object reference is safely published.
It is worth noting that this is a special rule in the Java Memory Model. The visibility guarantees for final fields are stronger than those for ordinary fields and do not rely on explicit synchronization such as volatile or synchronized. Because this topic involves deeper memory model semantics and subtle edge cases, it deserves a dedicated discussion. I will cover it in a separate article. For readers who want to explore further, useful keywords to search include: Java final field semantics, safe publication, this escape during construction, JSR-133 memory model revision, and immutable objects and the Java Memory Model.
Summary
Happens-before is more than a theoretical rule in the specification. It explains why synchronized, volatile, start(), and join() work the way they do.
Most subtle concurrency bugs are not caused by complex algorithms, but by missing happens-before relationships. Once you understand this model, debugging and designing concurrent systems become significantly clearer.
Mastering happens-before means mastering visibility in Java.
Thank you for reading until the end!
If you found this article helpful, please subscribe and share it with anyone might benefit from it.