32

Given the following class:

class Foo {
  public volatile int number;

  public int method1() {
    int ret = number = 1;
    return ret;
  }

  public int method2() {
    int ret = number = 2;
    return ret;
  }
}

and given multiple threads calling method1() and method2() concurrently on the same Foo instance, can a call to method1() ever return anything other than 1?

Ellen Spertus
  • 6,576
  • 9
  • 50
  • 101
BeeOnRope
  • 60,350
  • 16
  • 207
  • 386

3 Answers3

15

I think the answer depends on the compiler. The language specifies:

At run-time, the result of the assignment expression is the value of the variable after the assignment has occurred.

I suppose that theoretically the value could be changed before the second (leftmost) assignment occurs.

However, with Sun's javac compiler, method1 will will turn into:

0:   aload_0
1:   iconst_1
2:   dup_x1
3:   putfield        #2; //Field number:I
6:   istore_1
7:   iload_1
8:   ireturn

This duplicates the constant 1 on the stack and loads it into number and then into ret before returning ret. In this case, it won't matter if the value stored in number is modified before assignment to ret, because 1, not number is being assigned.

Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
  • The language is confusing because it seems to imply that the "value" of the variable is used, seeming to imply that a second read is possible? – BeeOnRope Oct 12 '12 at 00:56
  • The bytecode is certainly interesting, but doesn't prove what is _required_ by the spec. – BeeOnRope Oct 12 '12 at 00:57
  • @BeeOnRope - On the surface, it appears to me that the code generated by the compiler is not strictly in accord with the language spec. However, it may be that "after the assignment has occurred" means "immediately after" and that any further changes to the variable from other threads do not affect the results. If that's what is meant (and it's not clear to me that it is), then javac is generating correct code, insulating the value from concurrent changes to `number`. – Ted Hopp Oct 12 '12 at 01:00
  • @Ted Hopp Perhaps you should add the following sentence from the spec. It might clarify things. – Guido Simone Oct 12 '12 at 01:01
  • Well even if the language does allow a return value of 2 due to a re-read of number, I don't think the code you showed is out of spec since for at least some moment during the lifetime of the statement number must have been 1. Absent any other guarantees about what value should be chosen if the value changes during the scope of the statement, it seems fine to choose 1. – BeeOnRope Oct 12 '12 at 01:18
  • IMHO, with the volatile keyword, the value of 'number' cannot be guaranteed and the variable should be de-referenced again. – Zagrev Oct 12 '12 at 02:01
  • @GuidoSimone - I'm not sure what clarity the following sentence would add. It just says that the result of an assignment is not a variable, meaning that it (the result) cannot be assigned to--it cannot appear as the left argument to an assignment operator. – Ted Hopp Oct 12 '12 at 06:30
10

The JLS 15.26 specifies:

There are 12 assignment operators; all are syntactically right-associative (they group right-to-left). Thus, a=b=c means a=(b=c), which assigns the value of c to b and then assigns the value of b to a.

Ted Hopp's answer shows that Sun's javac doesn't follow this behaviour, possibly as an optimisation.

Due to the threading here, the behaviour of method1 would be undefined. If Sun's compiler makes the behaviour constant then it doesn't break from the undefined behaviour.

  • Might this optimization may be permitted for [`volatile` fields](http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.4)? – trashgod Oct 12 '12 at 01:09
  • 1
    I'm curious though, if that's what the spec intended. I think they may have specified b in "and then assigns the value of b to a" just to make it clear the in the case that a, b and c have different types (say primitive integral/floating types) and conversions occur, the assignment occurs as-if the converted value was used. E.g., if a and c were doubles and b was a float. – BeeOnRope Oct 12 '12 at 01:16
  • @BeeOnRope That's quite possible - that JLS page appears to be more concerned with type conversions than with volatile fields. –  Oct 12 '12 at 01:27
  • I've accepted your answer, because I can't in good conscience accept the highest voted answer (sorry Ted - although I upvoted you too), because "what the this compiler does" doesn't tell you about what guarantees the language actually provides. FWIW, I've used this idiom since I asked this, with the assumption that there is *not* a read of `number`, and that the rightmost value (1 or 2 in this example) is always assigned to all the variables to the left of the rightmost =, because it's clean and neat to write it this way, and I've never seen a compiler which compiles in a read. – BeeOnRope Jan 08 '14 at 09:11
  • ... but I agree with @Bringer128 that I can't find language in the JLS to strictly support this behaviour. – BeeOnRope Jan 08 '14 at 09:13
5

Either the statement contains a volatile read, or it doesn't contain a volatile read. There cannot be any ambiguity here, since volatile read is very important to program semantics.

If javac can be trusted, we can conclude that the statement doesn't involve a volatile read of number. The value of an assignment expression x=y is really just the value of y (after conversions).

We can also deduce that

    System.out.println(number=1);

does not involve reading number

    String s;

    (s="hello").length();

does not involve reading s

    x_1=x_2=...x_n=v

does not involve reading x_n, x_n-1, ...; instead, the value of v is directly assigned to x_i (after necessary conversions through types of x_n, ... x_i

irreputable
  • 44,725
  • 9
  • 65
  • 93
  • I hope you are right, but the spec certainly doesn't give much weight to that interpretation, does it? I think volatile is a red herring. It could contain a non-volatile read, and still do weird things with threading (volatile means certain things must happen, but the same things *can* happen with a normal read). – BeeOnRope Oct 24 '12 at 01:51
  • This answer sounds right to me in practice. The JLS does specifically state that `a = b = c` is equivalent to `b = c; a = b;` but they appear to have implemented it as `b = c; a = c;` to prevent the need for a slow volatile field retrieval. With that said, the quoted JLS reference may just be a poor example rather than a *the compiler must do this* reference. –  Jan 09 '14 at 00:29