2

I have theoretical question about memory visibility. Here is sample code:

public class TwoThreadApp {

    private static class A {
        int x = 1;
    }

    public static void main(String[] arg) throws InterruptedException {
        A a = new A();

        Thread t2 = new Thread(() -> {
            while (true) {
                if (a.x == 2) {
                    System.out.println(a.x);
                    return;
                }
                // IO operation which makes a.x visible to thread "t2"
                System.out.println("in loop");
            }
        });

        t2.start();
        Thread.sleep(100);
        a.x = 2;
    }
}
  1. Without System.out.println("in loop") programs works indefinitely, which is expected behavior.
  2. But with System.out.println("in loop") it is always completes, which is not expected, because a.x is not volatile and there is no synchronized blocks.

My env: ubuntu 16.04, openjdk 1.8.0_131

Why it behaves this way?

Sergey Galkin
  • 159
  • 3
  • 8

2 Answers2

0

Without System.out.println("in loop") programs works indefinitely, which is expected behavior.

On the contrary, the program should quit. The program keeps going, that is side-effect of fact that x is cached (as non volatile). The caching is compiler's optimalization and you should not rely on it (depending on JVM settings).

But with System.out.println("in loop") it is always completes, which is not expected, because a.x is not volatile and there is no synchronized blocks.

This is IMHO expected behaviour. I cannot tell you why, I'd assume the IO operations are involved to clear the thread cache (please comment/correct if someone is having better insight).

Accessing variables without synchronization or locks or being volatile with multiple threads may be really unpredictable.

You can even disable many optimalizations with -Djava.compiler=NONE (then the program should always quit as expected)

gusto2
  • 11,210
  • 2
  • 17
  • 36
  • The main question is how IO operations involved in clearing thread cache. – Sergey Galkin Oct 31 '17 at 10:08
  • That was only my assumption. If you'd decompile, the bytecode for println will state `ldc` (load static data) and `invokevirtual` (call a method). The caching is really *magic* done by JIT (just-in-time compiler). Simply using no volatile or synchronized you make the program unpredictable – gusto2 Oct 31 '17 at 10:19
  • @SergeyGalkin, it has nothing to do with IO, but with the happens-before relationship applied by the JVM. The link to the duplicate response explains how println works (find "The effect of a print statement"), it includes a synchronized block. As per the Java memory model (see. https://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#synchronization): "Before we can enter a synchronized block, we acquire the monitor, which has the effect of invalidating the local processor cache so that variables will be reloaded from main memory." – Galo Navarro Jan 04 '18 at 19:08
0

IMHO this is something to do with Compiler optimization I think. In your first example compile can decide to move the If condition outside while loop like

if(a.x == 2) while(true) {...}

In 2nd case as you are using println which internally uses synchronized keyword compiler may not optimize the code as above.

This is what I think and I maybe wrong.

Edit: You can also refer here : Loop doesn't see changed value without a print statement

Neerav Vadodaria
  • 317
  • 1
  • 11