3

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;
     }
 }
1
  • 1
    Threads can cache the value of non-volatile variables. Commented Apr 21, 2018 at 0:17

2 Answers 2

4

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.

Sign up to request clarification or add additional context in comments.

Comments

1

The value of ready may be updated but the other thread may never know about it. There you need volatile variables! A thread assumes that the variable is only used by this and only thread. So, it reads its value from the stack that it created.

private static volatile boolean ready;

What volatile does is that it says to your program to ready from the memory, not from the stack.

Actually what jvm does is it translates:

while(flag){...}

To:

if(flag){
    while(true){
}

The stack is created when the thread is created. It collectes the values of the variables in order to use them later.

This is what I have understand, correct me if I am wrong!

8 Comments

I have implemented entire cross-thread data synchronization systems without ever using volatile, so I'm pretty sure you don't need it, but from everywhere that I've read it's pretty strongly reccommended. Also, I will note that I never actually used the desktop implementation of JVM; it was always Android or something, so my note on not using volatile may not be true in other circumstances. So, I guess that using volatile will absolutely ensure that your value changes for that thread, but it might do so anyways.
I am not very experinced on android, but certainly this is the case. On your systems you somehow had shared variables or implemented a way to ensure reading from memory yourself. In example, if you add a print statement of the value of ready inside while(!ready) everything works fine (except from the fact that your console is ruined). Volatile makes sure things wont get ruined for no reason.
This means that there are also other ways to fix this issue, but volatile is by far the most easy to implement way.
Yeah, now that I think about it, I did have a system to fetch the latest value, but it was actually because the value needed to be calculated on access, based on context that the synchronization system didn't have access to. Fortunate side-effect I guess.
Re, "from the memory, not from the stack" The stack is a data structure in memory. You are correct in thinking that each thread has its own stack, but the stack is not something that "collects the values of the variables in order to use them later." The stack is where all of the program's local variables and function arguments are stored. All of the static variables and member variables in a Java program are located within a different part of memory which is known as the heap.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.