Java
instruction reordering
concurrency issues
thread safety
programming bugs

How to demonstrate Java instruction reordering problems?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Java allows compilers, the JVM, and the CPU to reorder operations when single-threaded behavior is preserved. In multithreaded code without a proper happens-before relationship, that freedom can expose results that look impossible, which is why instruction reordering is usually demonstrated with a small shared-memory test.

A Classic Reordering Demonstration

The usual example has two threads writing one variable and reading the other. If no synchronization is present, the program may eventually observe both reads as zero even though each thread performed a write first in source order.

java
1public class ReorderingDemo {
2    static int x = 0;
3    static int y = 0;
4    static int a = 0;
5    static int b = 0;
6
7    public static void main(String[] args) throws Exception {
8        int iterations = 0;
9
10        while (true) {
11            iterations++;
12            x = y = a = b = 0;
13
14            Thread one = new Thread(() -> {
15                a = 1;
16                x = b;
17            });
18
19            Thread two = new Thread(() -> {
20                b = 1;
21                y = a;
22            });
23
24            one.start();
25            two.start();
26            one.join();
27            two.join();
28
29            if (x == 0 && y == 0) {
30                System.out.println("Observed reordering after " + iterations + " iterations");
31                break;
32            }
33        }
34    }
35}

This program is valid Java, but the outcome is not guaranteed to appear quickly. You may need many iterations, and on some machines it can take a long time. That does not make the example wrong; it only means the scheduler and hardware timing have to align before the weakly ordered behavior becomes visible.

Why the Result Is Allowed

Each thread looks simple in isolation. Thread one writes a = 1 and then reads b. Thread two writes b = 1 and then reads a. Without volatile, synchronized, or another memory-ordering construct, there is no rule forcing one thread to observe the other thread's write before doing its own read.

That means the system can legally produce this effect:

  • thread one reads the old value of b
  • thread two reads the old value of a

The surprising part is not only compiler reordering. Cache visibility, store buffers, and general lack of ordering also contribute. In practice, people often say "instruction reordering" as a shorthand for the whole category of memory-ordering problems.

Making the Example Deterministic Enough to Study

If you want to make the race easier to trigger, align the thread starts more closely. A barrier reduces the chance that one thread finishes most of its work before the other even begins.

java
1import java.util.concurrent.CyclicBarrier;
2
3public class ReorderingWithBarrier {
4    static int x = 0;
5    static int y = 0;
6    static int a = 0;
7    static int b = 0;
8
9    public static void main(String[] args) throws Exception {
10        int iterations = 0;
11
12        while (true) {
13            iterations++;
14            x = y = a = b = 0;
15            CyclicBarrier barrier = new CyclicBarrier(3);
16
17            Thread one = new Thread(() -> runFirst(barrier));
18            Thread two = new Thread(() -> runSecond(barrier));
19
20            one.start();
21            two.start();
22            barrier.await();
23            one.join();
24            two.join();
25
26            if (x == 0 && y == 0) {
27                System.out.println("Observed after " + iterations + " iterations");
28                break;
29            }
30        }
31    }
32
33    static void runFirst(CyclicBarrier barrier) {
34        try {
35            barrier.await();
36            a = 1;
37            x = b;
38        } catch (Exception e) {
39            throw new RuntimeException(e);
40        }
41    }
42
43    static void runSecond(CyclicBarrier barrier) {
44        try {
45            barrier.await();
46            b = 1;
47            y = a;
48        } catch (Exception e) {
49            throw new RuntimeException(e);
50        }
51    }
52}

This still does not guarantee the 0, 0 observation, but it makes the demonstration more practical.

Fixing the Problem

The correct lesson is not "avoid thread scheduling." The lesson is to establish a happens-before relationship. One straightforward fix is to mark the shared variables as volatile or guard the critical section with synchronized.

java
1public class ReorderingFixed {
2    static volatile int a = 0;
3    static volatile int b = 0;
4    static int x = 0;
5    static int y = 0;
6}

Once ordering is explicit, the weird result is no longer allowed by the Java Memory Model.

Common Pitfalls

  • Expecting the reordering outcome to appear immediately on every machine.
  • Confusing lack of visibility with only compiler optimization.
  • Using a demo without synchronization and then assuming the outcome proves a JVM bug.
  • Fixing the sample with sleeps instead of proper memory-ordering primitives.
  • Forgetting that join only orders the main thread after worker completion, not the worker operations relative to each other.

Summary

  • Java can expose surprising shared-memory results when code has a data race.
  • The classic two-thread litmus test shows how both reads can observe stale values.
  • The effect comes from missing ordering guarantees, not from one single optimization stage.
  • A barrier can make the demo easier to observe, but it does not fix the race.
  • Use volatile, synchronized, or higher-level concurrency tools to make ordering explicit.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.