2

As expected the reader thread in below program runs forever since it cached the stop flag(non-volatile) on its local processor's cache. Whereas, as soon as I uncomment the println on reader thread, the flag gets updated flag value and program stops. How this is possible as writer thread only writes flag to its own local cache and not yet flushed to main memory?

Note: running this program in MacBook Pro x86 architecture machine.

public class FieldVisibility {
    boolean stop = false;
    public static void main(String[] args) throws Exception {
        FieldVisibility fv = new FieldVisibility();

        Runnable writerThreadJob = () -> {  fv.writer();    };
        Runnable readerThreadJob = () -> {  fv.reader();    };

        Thread writerThread = new Thread(writerThreadJob);
        Thread readerThread = new Thread(readerThreadJob);

        readerThread.start();
        try {   Thread.sleep(2);    } catch (InterruptedException e) {}
        writerThread.start();
    }

    private void writer() {
        stop = true;
    }

    private void reader() {
        while (!stop) {
//          System.out.println("stop is still false...");
        }
    }
}
  • 1
    Caches are coherent on x86, that's not a cache issue. Memory barriers are used to *order* memory access (store-loads) on x86, visibility is automatic and anyway never delayed indefinitely. The threads are also probably running in the same core/hardware-thread. It's probably a JIT issue: it transforms the reader loop into an infinite loop while it doesn't do the same optimization if some code (especially a call to an "external" method) is present. The JVM is allowed to never return if you don't use `volatile` but it is not obliged to do so, it doesn't know `println` won't change `stop`. – Margaret Bloom Apr 19 '20 at 16:44
  • You *didn't use* `volatile` despite tagging it. That's why (with an otherwise empty loop) Java can read `stop` once before the loop and then keep checking a private copy. The C++ equivalent is [Multithreading program stuck in optimized mode but runs normally in -O0](https://stackoverflow.com/q/58516052) - in C++ you can disable optimization to make broken code like this work the way you expected. (Java `volatile` is like C++ `atomic`, not at all like C++ `volatile`. I assume there's a duplicate Java Q&A; maybe a `[java]` user will know of one) – Peter Cordes Apr 19 '20 at 18:06

1 Answers1

1

Margaret Bloom and Peter Cordes already gave the answer in the comments section.

The JIT is allowed to hoist the stop variable out of the loop because it isn't volatile. This optimization is called loop invariant code motion.

So the following loop:

private stop;

private void reader() {
    while (!stop) {
         System.out.println("stop is still false...");
    }
}

Can be transformed to:

private stop;

private void reader() {
    if(stop) return;

    while (true) {
         System.out.println("stop is still false...");
    }
}

And now it is obvious the loop will never end.

pveentjer
  • 7,395
  • 3
  • 18
  • 31