javaandroidhotspothappens-beforenon-volatile

Do Android ART and HotSpot behave differently in non-volatile variable visibility?


I tested the below piece of code on HotSpot and Android ART, but with different results.

On HotSpot, MyThread never gets the updated isRunning, it get isRunning = true always... But when I test it on ART, MyThread can get the updated isRunning and exit loop normally...

As I know about java happens-before rule, an non-volatile is not visible across multi-thread, just like the behave of the code below on Hotspot.

Does it depends on VM implementation? Or maybe Android ART has their own optimization?

class MyThread extends Thread {
    public boolean isRunning = true;

    @Override
    public void run() {
        System.out.println("MyThread running");
        while (true) {
            if (isRunning == false) break;
        }
        System.out.println("MyThread exit");
    }
}

public class RunThread{
    public static void main(String[] args) {
        new RunThread().runMain();
    }

    public void runMain() {
        MyThread thread = new MyThread();
        try {
            thread.start();
            Thread.sleep(500);
            thread.isRunning = false;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


Solution

  • an non-volatile is not visible across multi-thread, just like the behave of the code below on Hotspot.

    That's not quite right. A non-volatile write, absent any additional synchronization or other happens-before relationship, is not guaranteed to be visible to a read of that same variable in another thread. It's allowed to be visible, though. I've absolutely seen HotSpot make writes visible across threads despite no happens-before relationship. Based on my experience with this, I suspect that if you remove that Thread.sleep call in your code, HotSpot will also make the write to isRunning visible to the thread, despite the lack of any happens-before relationship between the write and the read.

    You're definitely right that it's VM-specific, and it's possibly/likely even processor architecture–specific, since different architectures may give different amounts of synchronization for "free", or have different amounts of cache that affect whether a memory address is read from a core's cache or fetched from main memory.

    In summary, you should never rely on this sort of behavior working any specific way on any specific VM—it's liable to change without warning.