[concurrency-interest] Volatile access used for observing nonvolatile changes

Boehm, Hans hans.boehm at hp.com
Mon Apr 25 16:02:59 EDT 2011


The code is incorrect, but the results are not surprising.  A compiler that only looks at the body of t0 is no longer allowed to move the read of data out of the loop.  If another thread were to set sync after setting data, then t0 would be required to see data as true after reading the updated value of sync.

If you had a cleverer compiler that noticed that sync were never actually set, it could legitimately eliminate the read of sync, and turn this back into an infinite loop.  A more serious problem in practice is likely to be that if you read another variable data2 after the wait loop, the read of data2 may appear to occur before data was set.  In a sense, the wait loop isn't guaranteed to wait as long as you really want.  Plus you have racy code, and none of us really knows what data races mean in Java.

Bottom line:  Don't do this!

Hans

From: concurrency-interest-bounces at cs.oswego.edu [mailto:concurrency-interest-bounces at cs.oswego.edu] On Behalf Of Attila Szegedi
Sent: Monday, April 25, 2011 12:36 PM
To: concurrency-interest at cs.oswego.edu
Subject: [concurrency-interest] Volatile access used for observing nonvolatile changes

So, I have a coworker who's using access to a volatile field to observe changes to a non-volatile field from a different thread. I tried to dissuade him saying there's no guarantee this should always work, he insists this is a sound practice. I'd be interested in the insight of folks on this list. I whittled it down to a very simple example... First, here's a code example that, quite expectedly, doesn't terminate, as t0 never observes the change made in t1:

public class VolatileTest {
  private boolean data;

  public static void main(String[] args) {
    new VolatileTest().run();
  }

  private void run() {
    Thread t0 = new Thread() {
      public void run() {
       while(!data);
      }
    };

    Thread t1 = new Thread() {
      public void run() {
       data = true;
      }
    };

    t0.start();
    t1.start();
  }
}

However, adding a volatile variable unrelated to the "data" variable and just reading it from t0 does terminate. Note that the volatile variable was never even written from t1!

public class VolatileTest {
  private boolean data;
  private volatile boolean sync;

  public static void main(String[] args) {
    new VolatileTest().run();
  }

  private void run() {
    Thread t0 = new Thread() {
      public void run() {
       while(!data) {
         boolean x = sync;
       }
      }
    };

    Thread t1 = new Thread() {
      public void run() {
       data = true;
      }
    };

    t0.start();
    t1.start();
  }
}


I thought there might be a cache granularity issue here, so I replaced "boolean data" with a "boolean[ ] data = new boolean[10*1024*1024], and was setting its last element, but it didn't cause a difference. I tried to find an explanation of this behavior from my understanding of JLS 17.4.4 "Synchronization Order" and clauses around it, but couldn't, specifically since t1 never touches the volatile variable.

Can someone give me an example where this program would again not terminate - that is, t0 wouldn't observe a change to a non-volatile variable even if it did a read of a volatile variable?

This is happening on a Core 2 Duo CPU with 3MB of L2 cache, on Mac OS X 10.6.7, java is:

java version "1.6.0_24"
Java(TM) SE Runtime Environment (build 1.6.0_24-b07-334-10M3326)
Java HotSpot(TM) 64-Bit Server VM (build 19.1-b02-334, mixed mode)

Thanks for any insights,
  Attila.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://cs.oswego.edu/pipermail/concurrency-interest/attachments/20110425/3facf40e/attachment-0001.html>


More information about the Concurrency-interest mailing list