7

Consider the Java example below. Notice that neither of the class member variables are declared to be volatile. If I am understanding the memory model and "happens before" rules correctly, a Java implementation could optimize the run() method so that it runs forever, even if another thread calls the stopNow() method. This can happen because there is nothing in the run() method that forces the thread to read the value of stop more than once. Is that correct? If not, why not?

class Example implements Runnable {
    boolean stop = false;
    int value = 0;

    public void stopNow() {
       stop = true;
    }

    public int getValue() {
        return value;
    }

    @Override
    public void run() {
        // Loop until stop is set to true.
        while (!stop) {
            ++value;
        }
        return;
    }
}
Daryl Odnert
  • 522
  • 3
  • 15
  • Shopuld be correct, yes. I'm not sure if that's a "can" or "have to" hwoever, so maybe third-party VMs may handle this differently. – Johannes H. Feb 05 '14 at 04:23

2 Answers2

4

that can be modified by another thread but this is not gurranteed. also this is not thread safe either. To make a variable gurranteed to see from another thread you need to do any of the following

Changes to fields made by one thread are guaranteed to be visible to other threads only under the following conditions:

  • A writing thread releases a synchronization lock and a reading thread subsequently acquires that same synchronization lock.
  • If a field is declared as volatile, any value written to it is flushed and made visible by the writer thread before the writer
    thread performs any further memory operation (i.e., for the purposes
    at hand it is flushed immediately). Reader threads must reload the
    values of volatile fields upon each access.
  • The first time a thread accesses a field of an object, it sees either the initial value of the field or a value since written by some other thread.
  • As a thread terminates, all written variables are flushed to main memory. For example, if one thread synchronizes on the termination of another thread using Thread.join, then it is guaranteed to see the
    effects made by that thread (see §4.3.2).

helpful SO thread

Community
  • 1
  • 1
stinepike
  • 54,068
  • 14
  • 92
  • 112
  • 1
    It is interesting that even if `stop` were change to be a public member and, inside the while loop, a call was made to a method in another class or package, there is still no Java language requirement for the value of `stop` to be fetched again from memory. But I do think the compiler would need to examine the called code to know that such an optimization was safe. In the absence of a thorough analysis, the compiler would probably not optimize away the memory fetch for `stop`. – Daryl Odnert Feb 05 '14 at 05:42
  • 2
    @DarylOdnert "*he compiler would need to examine the called code to know that such an optimization was safe*" => no it wouldn't: not reading it from memory is allowed by the memory model so even if it leads to a bug (I suppose that's your definition of safe) it is allowed. Without volatile, the only guarantee is the program order, per thread. – assylias Feb 05 '14 at 11:54
  • @assylias - That doesn't sound right. If the while loop contains a function call (passing `this` as a parameter) and the called function modifies `this.stop`, the memory model says that the update must be recognized because the assignment happens _in the same thread_. Am I missing something? – Daryl Odnert Feb 05 '14 at 18:11
  • @DarylOdnert If it's in the same thread then yes, the change is guaranteed to be visible. – assylias Feb 05 '14 at 18:21
  • @assylias - So you agree with me, then, that a compiler cannot optimize away the memory fetch for the value of `stop` unless it is absolutely sure that no called functions will modify its value, right? – Daryl Odnert Feb 05 '14 at 18:29
  • 2
    @DarylOdnert if a method modifies stop in thread T1 then the change will be visible to any subsequent code running in T1 - but that does not mean the value is fetched from memory... If a thread T2 reads stop subsequently it may not see the new value. And if T3 had modified stop previously it may not be seen from T1... – assylias Feb 05 '14 at 23:08
  • @assylias - Thanks. That makes sense. The only subtle point I would add about your last sentence is that T1 **will** see the modification made in T3 if that modification is made before the first access of `stop` in T1. This is one of the rules that were quoted in the answer from @StinePike above. – Daryl Odnert Feb 06 '14 at 21:26
  • 1
    @DarylOdnert Not necessarily - it depends on when T1 is started. If T1 starts *after* stop is written to in T3, then yes it will see it. Otherwise, T1 may see the default value (false), or some other value that stop has had at some time, or the most recent one. Either is possible and allowed. – assylias Feb 06 '14 at 22:36
2

That's correct. That's one of the reasons you might want to use volatile.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • Maybe you can answer this while you are here. Why is it that in debug mode, if you blocked inside the `run()` after `stop` was turned `true` in another thread, the thread executing `run()` would see its value as `true`? Does Java's debug mode force refreshing the value? If it can, is there a way to do so in normal mode? If there is, then would your answer become wrong? – Sotirios Delimanolis Feb 05 '14 at 04:29
  • 2
    @SotiriosDelimanolis Programmers typically care most about what they can *rely* on, not what happens to work. – David Schwartz Feb 05 '14 at 04:37
  • If there is a way for to force Java to reread the value of `stop`, then the JVM cannot _optimize the `run()` method_, because the optimization would be wrong. – Sotirios Delimanolis Feb 05 '14 at 04:38
  • @DavidSchwartz I'm full on-board with the "use `volatile`" part but telling the OP that they're correct is a bit disingenuous. I don't believe there's an environment where what they describe (the thread will run forever) would actually occur. It might run *slightly longer* because it reads the stale `stop` value N times, but eventually the cache will sync. – Brian Roach Feb 05 '14 at 04:43
  • @SotiriosDelimanolis Right. One such way is to use `volatile`. There may be others, sure. You have to solve this somehow, and you can use any solution that's guaranteed to work on your platform/environment. – David Schwartz Feb 05 '14 at 04:45
  • 3
    @BrianRoach I've been running it since this question was posted, and the field hasn't been synced. – Sotirios Delimanolis Feb 05 '14 at 04:46
  • I'm saying without the use of `volatile`, do you know any other way to force it to be synced? – Sotirios Delimanolis Feb 05 '14 at 04:46
  • 1
    @BrianRoach The issue is what he can rely on, not what you believe is likely. How do you know what will happen when he upgrades his CPU, OS, our Java version? Why would you want to encourage him to write fragile, broken code? – David Schwartz Feb 05 '14 at 04:47
  • @SotiriosDelimanolis Check your platform documentation. It may provide some other way. There are also portable ways, such as other synchronization primitives. – David Schwartz Feb 05 '14 at 04:47
  • @SotiriosDelimanolis LOL, true story, I can't get it to *not* sync. on my machine. – Brian Roach Feb 05 '14 at 04:48
  • @DavidSchwartz I'm not (?) Not sure where you got that from. – Brian Roach Feb 05 '14 at 04:53
  • 4
    @BrianRoach "It might run slightly longer because it reads the stale stop value N times, but eventually the cache will sync." That is totally wrong. Almost all modern caches are instantaneously coherent in hardware. This has nothing to do with caches. – David Schwartz Feb 05 '14 at 04:55
  • @BrianRoach The risk here is not so much visibility - it is variable hoisting: the JVM may (and would probably) compile the code as `boolean local = stop; while (!local) {}`. – assylias Feb 05 '14 at 08:47