views:

231

answers:

5

I'm looking at a code sample from "Java Concurrency in Practice" by Brian Goetz. He says that it is possible that this code will stay in an infinite loop because "the value of 'ready' might never become visible to the reader thread". I don't understand how this can happen...

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;
    } 
}
+8  A: 

Because ready isn't marked as volatile and the value may be cached at the start of the while loop because it isn't changed within the while loop. It's one of the ways the jitter optimizes the code.

So it's possible that the thread starts before ready = true and reads ready = false caches that thread-locally and never reads it again.

Check out the volatile keyword.

Qberticus
+5  A: 

The reason is explained in the section following the one with the code sample.

3.1.1 Stale data

NoVisibility demonstrated on of the ways that insufficiently synchronized programs can cause surprising results: stale data. When the reader thread examines ready, it may see an out-of-date value. Unless synchronization is used every time a variable is accessed, it is possible to see a stale value for that variable.

Bill the Lizard
+3  A: 

The Java Memory Model allows the JVM to optimize reference accesses and such as if it is a single threaded application, unless the field is marked as volatile or the accesses with a lock being held (the story gets a bit complicated with locks actually).

In the example, you provided, the JVM could infer that ready field may not be modified within the current thread, so it would replace !ready with false, causing an infinite loop. Marking the the field as volatile would cause the JVM to check the field value every time (or at least ensure that ready changes propagate to the running thread).

notnoop
+1  A: 
private static boolean ready;
private static int number;

The way the memory model can work is that each thread could be reading and writing to its own copy of these variables (the problem affects non-static member variables too). This is a consequence of the way the underlying architecture can work.

Jeremy Manson and Brian Goetz:

In multiprocessor systems, processors generally have one or more layers of memory cache,which improves performance both by speeding access to data (because the data is closer to the processor) and reducing traffic on the shared memory bus (because many memory operations can be satisfied by local caches.) Memory caches can improve performance tremendously, but they present a host of new challenges. What, for example, happens when two processors examine the same memory location at the same time? Under what conditions will they see the same value?

So, in your example, the two threads might run on different processors, each with a copy of ready in their own, separate caches. The Java language provides the volatile and synchronized mechanisms for ensuring that the values seen by the threads are in sync.

McDowell
Thanks everyone - makes sense now. So this is specific to the way that the JVMs map the java code into executable code. I assume that similar concerns can arise in C++? Does anyone know how C++ handles this issue?
Andy Faibishenko
A: 

The problem is rooted in the hardware -- each CPU has different behavior with respect to cache coherence, memory visibility, and reordering of operations. Java is in better shape here than C++ because it defines a cross-platform memory model that all programmers can count on. When Java runs on a system whose memory model is weaker than that required by the Java Memory Model, the JVM has to make up the difference.

Languages like C "inherit" the memory model of the underlying hardware. There is work afoot to give C++ a formal memory model so that C++ programs can mean the same thing on different platforms.

Brian Goetz