javamultithreadingconcurrencythread-safetyjava-threads

java instance variable not visible to other threads


I've encountered this code in a book. It states NoVisibility could loop forever because the value of ready might never become visible to the reader thread.

I'm confused by this statement. In order for the loop to run forever, ready must always be false, which is the default value. This means it must fail at executing ready = true; because the reader thread will always read the ready variable from memory. the assignment happens in CPU and it must have some problem in flushing the data back to Main Memory. I think I need some explanation on a situation how it can fail, or I may have missed some other part.

public class NoVisibility {
     private static boolean ready;
     private static int number;

     private static class ReaderThread extends Thread {
         public void run() {
         while (!ready)
             Thread.yield();
             System.out.println(number);
         }
     }

     public static void main(String[] args) {
         new ReaderThread().start();
         number = 42;
         ready = true;
     }
 }

Solution

  • In short, the book is correct.

    You are assuming that Java will behave intuitively here. In fact, it may not. And, indeed, the Java Language specification allows non-intuitive behavior if you don't follow the rules.

    To be more specific, in your example it is not GUARANTEED that the second thread will see the results of the first thread's assignment to ready1. This is due to such things as:

    1. The compiler caching the value of ready in a register in the first or second thread.
    2. The compiler not including instructions to force the write to be flushed from one core's memory cache to main memory, or similar.

    If you want a guarantee that the second thread will see the result of the write then either reads and writes of ready by the two threads must be (properly) synchronized, or the ready variable must be declared to be volatile.

    So ...

    This means it must fail at executing ready = true; because the reader thread will always read the ready variable from memory.

    ... is incorrect. The "because" is not guaranteed by the Java language specification in this example.

    Yes. It is non-intuitive. Using your intuition based on an understanding of single-threaded programs is not reliable. If you want to fully understand what is and is not guaranteed, you need to study the specification of the "Java Memory Model" in Section 17.4 of the JLS. Warning: it is NOT an easy read.


    1 - It might see the results immediately, or after a short or long delay. Or it might never see them. And the behavior is liable to vary from one system to the next, and with versions of the Java platform. So your program that (by luck) works all of the time on one system may not always work on another system.