Volatile variable and non volatile reordering / vi

2019-04-03 01:14发布

问题:

So I thought I knew this stuff well enough, until I read something which got me doubting my knowledge on this subject matter. I am almost certain the book is incorrect but would like to ask the community as well.

PS: Have not seen the errata of the book so could well be disclosed as an error.

A simplified example:

public class VolatileMain {

private volatile int a = 0;
private String text = "";

public static void main(String[] args) throws Exception {

    VolatileMain vm = new VolatileMain();

    Thread writer = new Thread() {

        @Override
        public void run() {
            System.out.println("Running thread " + Thread.currentThread().getName());
            vm.text = "hello world";
            vm.a = 5;
        }
    };

    writer.start();
    writer.join();

    System.out.println("Running thread " + Thread.currentThread().getName());
    System.out.println(vm.a);
    System.out.println(vm.text);

   }

}

So given the example is it correct to assume that the write to "text" by Thread writer is guaranteed to be visible by any other thread that reads it?

It seems the author is piggy backing on the volatile semantics of the variable "a" and ensuring that the write to "text" will also be flushed when "a" is flushed, is this a guarantee?

I didn't think it was, but my own quick test (above) to the contrary

Your thoughts.

回答1:

is it correct to assume that the write to "text" by Thread writer is guaranteed to be visible by any other thread that reads it?

No. But it's guaranteed to be visible by any other thread that reads a before reading text, as your example does:

  • the write of text happens-before the write to a in the writer thread
  • the write of a in writer happens-before the read of a in the main thread
  • the happens-before relation is transitive
  • hence the the write of text happens before the read of a.


回答2:

No, it's not guaranteed, because the "flushing" is not that simple. Even if you actually write non-volatile something into the "main memory", it does not guarantee that consequent reads in other threads will read it from that main memory. Consider the following example:

public class VolatileMain {

    private volatile int a = 0;
    private String text = "";

    public static void main(String[] args) throws Exception {

        VolatileMain vm = new VolatileMain();

        Thread writer = new Thread() {

            @Override
            public void run() {
                // Added sleep here, so waitForText method has chance to JIT-compile
                LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
                System.out.println("Running thread " + Thread.currentThread().getName());
                vm.text = "hello world";
                vm.a = 5;
                System.out.println("Text changed!");
            }
        };

        writer.start();

        waitForText(vm);

        writer.join();

        System.out.println("Running thread " + Thread.currentThread().getName());
        System.out.println(vm.a);
        System.out.println(vm.text);

    }

    // Wait for text change in the spin-loop
    private static void waitForText(VolatileMain vm) {
        int i = 0;
    /*
        @Edit by Soner
        Compiler may do following steps to optimize in lieu.
        String myCache = vm.text;
        -- Assume that here myCache is "" -- so stay forever.
        while (myCache.equals("")) { i++; }
    */
        while (vm.text.equals("")) {
            i++;
        }
        System.out.println("Wait complete: " + i);
    }
}

There are quite good chances that waitForText will never finish, just because JIT-compiler will optimize it and move the reading of the vm.text out of the loop (as it's not volatile, no volatile reads are performed in the loop and text never changes inside the loop) making the loop infinite.

Volatile read/write not only affects the memory commitment, but also changes the JIT-compilation strategy. Add reading of vm.a in the while-loop and the program will work correctly.