33

给定以下课程:

class Foo {
  public volatile int number;

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

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

并且给定多个线程在同一个实例上同时调用method1(),对method1()的调用可以返回1以外的任何东西吗?method2()Foo

4

3 回答 3

16

我认为答案取决于编译器。语言规定

在运行时,赋值表达式的结果是赋值发生后变量的值。

我想理论上这个值可以在第二个(最左边的)赋值发生之前改变。

但是,使用 Sun 的 javac 编译器,method1会变成:

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

这将复制1堆栈上的常量并将其加载到number然后再加载到ret返回之前retnumber在这种情况下,如果在分配给 之前修改了存储的值,则无关紧要ret,因为分配的是1not number

于 2012-10-12T00:50:19.390 回答
10

JLS 15.26 规定:

有12个赋值运算符;所有在语法上都是右关联的(它们从右到左分组)。因此,a=b=c 表示 a=(b=c),将 c 的值赋给 b,然后将 b 的值赋给 a。

Ted Hopp 的回答表明 Sun 的 javac 不遵循这种行为,可能是作为一种优化。

由于这里的线程,method1 的行为将是未定义的。如果 Sun 的编译器使行为保持不变,那么它不会中断未定义的行为。

于 2012-10-12T00:57:58.403 回答
6

该语句要么包含易失性读取,要么不包含易失性读取。这里不能有任何歧义,因为 volatile read 对于程序语义非常重要。

如果 javac 是可信的,我们可以得出结论,该语句不涉及对number. 赋值表达式x=y的值实际上只是y(转换后)的值。

我们也可以推断出

    System.out.println(number=1);

不涉及阅读number

    String s;

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

不涉及阅读s

    x_1=x_2=...x_n=v

不涉及阅读x_n, x_n-1, ...;相反, 的值v被直接分配给x_i(在通过类型的必要转换之后x_n, ... x_i

于 2012-10-12T17:05:56.827 回答