Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

Suppose there are two threads without synchronization, one sets n = 1 another executes method().

In the following "read" always refers to a read of field n.

public class MyClass
{
  public int n = 0;

  public void method() {
    System.out.println(n); //read 1
    System.out.println(n); //read 2
  }
}

Would the following output be possible?

1
0

The answer is yes because even though read 1 happens-before read 2, it is nevertheless possible for read 2 to be re-ordered before read 1 because it wouldn't change the semantics of intra-thread execution.

Is this reasoning correct?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
137 views
Welcome To Ask or Share your Answers For Others

1 Answer

Happens-before does not imply the order for two arbitrary operations. To be more precise, the most important thing that happens-before does is tying up writes and reads in happens-before consistency. Notably, it tells what writes can a read observe: the last write in happens-before order, or any other write not ordered in happens-before (race). Note that two consecutive reads may see different values obtained from different (racy) writes, without violating that requirement.

E.g. JLS 17.4.5 says:

It should be noted that the presence of a happens-before relationship between two actions does not necessarily imply that they have to take place in that order in an implementation. If the reordering produces results consistent with a legal execution, it is not illegal.

Data races are creepy like that: racy reads can return surprising data on each read, and Java memory model captures that. So the more precise answer is that an execution that produces (1, 0) is not violating Java Memory Model constraints (sync order consistency, sync order - program order consistency, happens-before consistency, causality requirements), and therefore allowed.

Implementation-wise: on hardware, both loads can be started and/or arrive to memory subsystem at different times, regardless of their "program order", because they are independent; in compilers, instruction scheduling may also disregard the program order for the independent reads, exposing loads to hardware in "counter-intuitive" order.

If you want reads to be observed in the program order, you need a stronger property. JMM gives that property to synchronization actions (in your example, making a variable volatile would make that), which ties the actions in the total synchronization order that is consistent with program order. In that case, (1, 0) would be prohibited.

Illustration on a very special jcstress testcase (see the full source for caveats):

private final Holder h1 = new Holder();
private final Holder h2 = h1;

private static class Holder {
    int a;
    int trap;
}

@Actor
public void actor1() {
    h1.a = 1;
}

@Actor
public void actor2(IntResult2 r) {
    Holder h1 = this.h1;
    Holder h2 = this.h2;
    h1.trap = 0;
    h2.trap = 0;
    r.r1 = h1.a;
    r.r2 = h2.a;
}

Even on x86 that does not reorder loads, yields (1, 0), oops:

      [OK] o.o.j.t.volatiles.ReadAfterReadTest                                                                                                      
    (fork: #1, iteration #1, JVM args: [-server])
  Observed state   Occurrences              Expectation  Interpretation                                              
          [0, 0]    16,736,450               ACCEPTABLE  Doing both reads early.                                     
          [1, 1]   108,816,262               ACCEPTABLE  Doing both reads late.                                      
          [0, 1]         3,941               ACCEPTABLE  Doing first read early, not surprising.                     
          [1, 0]        84,477   ACCEPTABLE_INTERESTING  First read seen racy value early, and the s...

Making Holder.a volatile would make (1, 0) to go away.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...