Java Memory Model - Reordering Problem

[Updated: Feb 7, 2017, Created: Jul 8, 2016]

As stated in a previous tutorial: without proper synchronization, Java Memory Model can permit actions to appear to execute in different orders from the perspective of different threads which sometimes makes unexpected results. There might be various reasons why operations might be delayed or appear to execute out of order but they all can be put into the general category of reordering problem.


Reordering Example without Synchronization


In this example Thread1 writes to two variables 'flg' and 'a' and thread2 conditionally prints value of 'a' if 'flg' is true.

A programmer, of course, will make logical decisions based on the code order he/she is writing. Given thread1's code order one will never expect that thread2 will print a = 2 as this value of 'a' is written by thread2 after setting 'flg' to false.

Here's the complete code:

public class ReorderExample {
    private int a = 1;
    private boolean flg = true;

    public void method1 () {
        flg = false;
        a = 2;
    }

    public void method2 () {
        if (flg) {
            System.out.println("a = " + a);
        }
    }

    public static void main (String[] args) {

        for (int i = 0; i < 100; i++) {

            ReorderExample reorderExample = new ReorderExample();

            Thread thread1 = new Thread(() -> {
                reorderExample.method1();
            });

            Thread thread2 = new Thread(() -> {
                reorderExample.method2();
            });

            thread1.start();
            thread2.start();
        }
    }
}

Note in above code we are repeating the whole processes 100 times to increase the chances of producing the problem we are investigation


Output:

a = 2
a = 1
a = 2
a = 2
a = 1
a = 1

Process finished with exit code 0

As seen in the output the thread2 is printing the unexpected value of 2 various times. Occasionally it is also printing value of 1, which is fine as thread1 might sometimes be slower than thread2, and thread2 is able to read the default values before thread1 changes them.

Note above output might vary machine to machine. If you cannot reproduce the problem, please try to change the outer loop in the main method to a greater value or try other machine.

The machine I'm using:
    OS Name	Microsoft Windows 10 Home
    Version	10.0.10586 Build 10586
    Processor	Intel(R) Core(TM) i7-5700HQ CPU @ 2.70GHz, 2701 Mhz, 4 Core(s), 8 Logical Processor(s)
    Installed Physical Memory (RAM)	16.0 GB

How to fix the reordering problem?

The reasons of reordering problems vary. It might be delayed write to main memory which makes the code appears to be reordered or might be because of real code ordering as a results of JIT compiler/processor code optimization.

Java Memory Model doesn't require a programmer to figure out the real low level reasons because those reasons vary on different JIT compilers and on different machine architectures. It requires a programmer to recognize the situations where reordering might happen and do proper synchronization.

In above example declaring both variables as volatile won't solve the problem, the reasons is volatile just solves the visibility problem but not ordering. So we have to use synchronized blocks (locks) to fix the issue

public class ReorderExampleWithSynchronized {
    private int a = 1;
    private boolean flg = true;

    public synchronized void method1 () {
        flg = false;
        a = 2;
    }

    public synchronized void method2 () {
        if (flg) {
            System.out.println("a = " + a);
        }
    }

    public static void main (String[] args) {

        for (int i = 0; i < 100; i++) {

            ReorderExampleWithSynchronized reorderExample =
                                             new ReorderExampleWithSynchronized();

            Thread thread1 = new Thread(() -> {
                reorderExample.method1();
            });

            Thread thread2 = new Thread(() -> {
                reorderExample.method2();
            });

            thread1.start();
            thread2.start();
        }
    }
}

Output:

a = 1
a = 1
a = 1
a = 1
a = 1
a = 1
a = 1

Process finished with exit code 0

Synchronizing the shared data access across threads achieves two things:

  1. Mutual exclusion of two thread execution over synchronized block. This achieves atomicity. A threads is not able to see incomplete writes (involving multiple variables) of other threads.
  2. Flushing of local thread memory to main memory at time when the lock is released (at the end of synchronized block).

Example Project:

Dependencies and Technologies Used :

  • JDK 1.8
  • Maven 3.0.4

Thread Reordering Example Select All Download
  • java-thread-reordering-example
    • src
      • main
        • java
          • com
            • logicbig
              • example

See Also