Fundamentals 14 min read

Mastering Java’s Happens‑Before: Unlock Visibility & Ordering in Multithreading

This article explains Java’s happens‑before rules, covering visibility, ordering, program order, volatile, monitor locks, start/join semantics, and provides concrete code examples to illustrate how these rules prevent common concurrency bugs.

Programmer DD
Programmer DD
Programmer DD
Mastering Java’s Happens‑Before: Unlock Visibility & Ordering in Multithreading

Solution

To write bug‑free concurrent code, developers need a memory model that is easy to understand and program against. This leads to two contrasting models: a strong memory model for programmers and a weak memory model for compilers and processors, allowing them to perform optimizations.

“There’s nothing a meeting can’t solve; if there is, schedule another meeting.” 😂

JSR‑133 experts propose a compromise: selectively disable caching and compiler optimizations using volatile , synchronized , final and the Happens‑Before principle.

The Java Memory Model (JMM) tells the JVM to obey the happens‑before rules while secretly applying its own strategies.

For reorderings that would change program results, the JMM requires compilers and processors to forbid them.

For reorderings that don’t affect results, the JMM permits them.

Happens‑before

The happens‑before rule constrains two operations: it does not require the first operation to execute before the second, only that the result of the first be visible to the second.

Consider the following code:

class ReorderExample {
  int x = 0;
  boolean flag = false;
  public void writer() {
    x = 42; //1
    flag = true; //2
  }
  public void reader() {
    if (flag) { //3
      System.out.println(x); //4
    }
  }
}

If thread A runs writer and thread B runs reader, x may be printed as 0 because operations 1 and 2 have no data dependency and can be reordered.

Adding volatile to flag changes the semantics: volatile boolean flag = false; Since Java 1.5, volatile gains stronger memory semantics that, together with the happens‑before rules, guarantee visibility and ordering.

Program Order Rule

In a single thread, each operation happens‑before any subsequent operation in that thread.

This rule reflects the “as‑if‑serial” semantics: as long as the result is unchanged, the actual execution order may vary.

Volatile Variable Rule

A write to a volatile field happens‑before any subsequent read of that field.

Example:

public class ReorderExample {
    private int x = 0;
    private int y = 1;
    private volatile boolean flag = false;
    public void writer() {
        x = 42; //1
        y = 50; //2
        flag = true; //3
    }
    public void reader() {
        if (flag) { //4
            System.out.println("x:" + x); //5
            System.out.println("y:" + y); //6
        }
    }
}

Operations 1 and 2 cannot be reordered after the volatile write (3), but they may be reordered with each other.

Monitor Lock Rule

Unlocking a lock happens‑before any subsequent locking of that same lock.

This explains the semantics of synchronized:

public class SynchronizedExample {
    private int x = 0;
    public void synBlock() {
        synchronized (SynchronizedExample.class) {
            x = 1; // write
        }
    }
    public synchronized void synMethod() {
        x = 2; // write
    }
}

After a thread releases the lock, another thread acquiring the same lock will see the updated value of x.

start() Rule

If thread A calls ThreadB.start() , that call happens‑before any action in thread B.
public class StartExample {
    private int x = 0;
    private int y = 1;
    private boolean flag = false;
    public static void main(String[] args) throws InterruptedException {
        StartExample startExample = new StartExample();
        Thread thread1 = new Thread(startExample::writer, "线程1");
        startExample.x = 10;
        startExample.y = 20;
        startExample.flag = true;
        thread1.start();
        System.out.println("主线程结束");
    }
    public void writer() {
        System.out.println("x:" + x);
        System.out.println("y:" + y);
        System.out.println("flag:" + flag);
    }
}

The child thread sees all writes performed before start().

join() Rule

If thread A successfully returns from ThreadB.join() , all actions in thread B happen‑before the return in thread A.
public class JoinExample {
    private int x = 0;
    private int y = 1;
    private boolean flag = false;
    public static void main(String[] args) throws InterruptedException {
        JoinExample joinExample = new JoinExample();
        Thread thread1 = new Thread(joinExample::writer, "线程1");
        thread1.start();
        thread1.join();
        System.out.println("x:" + joinExample.x);
        System.out.println("y:" + joinExample.y);
        System.out.println("flag:" + joinExample.flag);
        System.out.println("主线程结束");
    }
    public void writer() {
        this.x = 100;
        this.y = 200;
        this.flag = true;
    }
}

After join() returns, the main thread observes the writes performed by the child thread.

Summary

Happens‑before ensures that the result of a preceding operation is visible to the following operation, solving visibility and ordering issues in multithreaded programs.

The start and join rules provide communication mechanisms between parent and child threads.

From a memory‑semantic perspective, a volatile write‑read pair has the same effect as a lock’s release‑acquire pair, while synchronized additionally guarantees atomicity.

Transitivity Rule

If A happens‑before B and B happens‑before C, then A happens‑before C.

Illustrated by the diagram:

Transitivity diagram
Transitivity diagram

From the diagram we see that x = 42 and y = 50 happen‑before flag = true (Rule 1), and flag = true happens‑before the read of flag (Rule 2). By transitivity, the writes to x and y happen‑before the read of flag, guaranteeing their visibility.

Further Questions

What are the differences in CPU instructions generated by synchronized blocks versus synchronized methods?

How do daemon and non‑daemon threads affect JVM shutdown behavior?

What remaining doubts do you have about the happens‑before rules?

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaMemory ModelHappens-before
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.