Java reordering volatile write with non-volatile

Viewed 238

I have a question about the code segment bellow. It is possible to have a result [0, 1, 0] for the result (this is test executed with JCStress). So how this can happen? I think that the data write (data = 1) should be executed before write to guard2 in Actor2 (guard2 = 1). Am I right? I ask, because a lot of times I've read that instructions arround volatiles are not reordered. Moreover according to this: http://tutorials.jenkov.com/java-concurrency/volatile.html it is written the following:

The reading and writing instructions of volatile variables cannot be reordered by the JVM (the JVM may reorder instructions for performance reasons as long as the JVM detects no change in program behaviour from the reordering). Instructions before and after can be reordered, but the volatile read or write cannot be mixed with these instructions. Whatever instructions follow a read or write of a volatile variable are guaranteed to happen after the read or write.

So if we cannot reorder

  public class DoubleVolatileTest {

      volatile int guard1 = 0;
      int data = 0;
      volatile int guard2 = 0;

      @Actor
      public void actor1() {
          guard2 = 1;
          data = 1;
          guard1 = 1;
      }

      @Actor
      public void actor2(III_Result r) {
          r.r1 = guard1;
          r.r2 = data;
          r.r3 = guard2;
      }

  }

Thanks in advance!

1 Answers

First of all, this:

The reading and writing instructions of volatile variables cannot be reordered by the JVM...

means that volatile themselves can not be re-ordered (volatile with volatile that is); but with the caveat that

the JVM may reorder instructions for performance reasons as long as the JVM detects no change in program behaviour from the reordering.

In general, reasoning about re-orderings (that could or not be done) by a JVM is incorrect (I've read that instructions around volatiles are not reordered ...). Re-ordering/barriers/etc are not part of the JLS; instead it acts on the premises of happens-before rules and that is the only thing you should care about.

Your example, can indeed be simplified as said in the comments to:

@Outcome(id = "0, 0", expect = Expect.ACCEPTABLE, desc = "don't care about this one")
@Outcome(id = "1, 0", expect = Expect.ACCEPTABLE_INTERESTING, desc = "the one we care about")
@Outcome(id = "1, 1", expect = Expect.ACCEPTABLE, desc = "don't care about this one")
@Outcome(id = "0, 1", expect = Expect.ACCEPTABLE, desc = "don't care about this one")
@JCStressTest
@State
public class VolatileTest {


    private volatile int guard = 0;
    private int x = 0;


    @Actor
    void writeActor() {
        guard = 1; // volatile store

        // your reasoning is that these two operations should be re-ordered
        // unfortunately, this is not correct.

        x = 1; // plain store
    }

    @Actor
    void readActor(II_Result r) {

        r.r1 = x; // plain store
        r.r2 = guard; // plain store

    }
}

Running this will result in 1, 0 indeed, which means that x = 1 was indeed re-ordered with guard = 1; actually in reality many more other things could have happened (but for simplicity we call them re-orderings, even though this is not the only reason why you could observe [1, 0]).

In JLS terms : you have not established any happens before between these operations (like a typical volatile store/volatile load) - as such those operations are free to float around. And that could be the end of the answer, pretty much. A little bit broader explanation would be that volatile (since you used it), is said:

A write to a volatile field happens-before every subsequent read of that same field.

You are not doing any reads of the volatile guard, so nothing is guaranteed. Another way to explain it would be this excellent article or even this one. But even if you did read guard, still nothing is guaranteed about re-orderings because the way your code is set-up.


volatile only works correctly when there are pairs of its usage, that is Thread1 does a write to a volatile field - Thread2 observes the write. In this case everything that was done before the write in program order will be seen by Thread2 (obviously after it has seen that written value). Or in code:

 public class VolatileTest {

    private volatile int guard = 0;
    private int x = 0;


    @Actor
    void writeActor() {

        // store comes "before" the store to volatile
        // as opposed to the previous example
        x = 1; // plain store
        guard = 1; // volatile store
    }

    @Actor
    void readActor(II_Result r) {

        r.r1 = guard; // plain store
        r.r2 = x; // plain store

    }
}

You are now guaranteed by the JLS that if you see guard to be 1, you will also observe x to be 1 (x = 1 can not be re-ordered below guard = 1 this time). As such, 1, 0 now is illegal and thus never seen in the output.

Related