what does Java Volatile Read really do?

Viewed 105

I have a very confused question about java volatile read.

I will show two cases to explain my question.

case1:

class TestVolatile {
    public boolean running = true;
    public volatile boolean volatileField;

    void run() {
        while(running) {
        }
        System.out.println("stopped.");
    }

    public static void main(String[] args) throws InterruptedException {
        TestVolatile t = new TestVolatile ();

        Thread t1 = new Thread(t::run, "t1");
        t1.start();
        TimeUnit.SECONDS.sleep(1);

        t.running = false;
        t1.join();
    }
}

case1 won't stop, This is completely understandable because running is not volatile.

Next I will show the second case.

case2:

class TestVolatile {
    public boolean running = true;
    public volatile boolean volatileField;

    void run() {
        while(running) {
            // just add a volatile read, the code will stop . 
            if (volatileField) {
                
            }
        }
        System.out.println("stopped.");
    }

    public static void main(String[] args) throws InterruptedException {
        TestVolatile t = new TestVolatile ();

        Thread t1 = new Thread(t::run, "t1");
        t1.start();
        TimeUnit.SECONDS.sleep(1);

        t.running = false;
        t1.join();
    }
}

just as case2 shows, after add a volatile read in the while loop, the code will stop.

so, why? running is not a volatile field and the code does not write volatileField. is there some happen-before relation can explain case2? thanks a lot for any help!

From the perspective of JMM, why? From the perspective of native code, why?

I have added ** -XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,*TestVolatile.run -XX:PrintAssemblyOptions=intel ** to jvm option to see native code of TestVolatile.run. But I don't see any special instruction about the volatile read.

my runtime:

java version "1.8.0_151"
Java(TM) SE Runtime Environment (build 1.8.0_151-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.151-b12, mixed mode)

macOS catalina 10.15.5
2.6 GHz 6-Core Intel Core i7 
1 Answers

Reading a volatile field doesn't only ensure you see the most up-to-date version of it. It also establishes a formal happens-before relationship: any write to the field happens-before a subsequent read to it. That happens-before means the reading thread sees at least the same state of the full program as the writing thread saw at the time of the write. That's what's causing the reading thread to see the non-volatile write.

These same semantics are what also let you publish non-thread-safe objects across threads, as long as they're not modified after they're published. And they're the same semantics that various thread-safe collections create, as described in the docs for java.util.concurrent.

Now, in your code, you don't actually write to volatileField after setting running to false. So, where is the happens-before edge come from? Actually -- nowhere! You still have a data race, and the JVM doesn't have to show you the most recent version of running. But many JVM implementations just treat a volatile read as "okay, let's flush all the core caches to make sure this thread sees everything," and you're benefiting from that behavior. A JVM with a more stingy and precise application of the JMM could break your code.

Related