在并发代码中返回赋值运算符的值

鉴于以下课程:

class Foo { public volatile int number; public int method1() { int ret = number = 1; return ret; } public int method2() { int ret = number = 2; return ret; } } 

并给予多个线程在同一个Foo实例上同时调用method1()method2() ,那么对method1()的调用是否会返回除1之外的任何内容?

JLS 15.26规定:

有12个分配操作员; 都是语法上的正确联想(他们从右到左)。 因此,a = b = c意味着a =(b = c),它将c的值赋给b,然后把b的值赋值给a。

Ted Hopp的回答显示,Sun的javac并没有遵循这种行为,可能作为优化。

由于这里的线程,方法1的行为将是不确定的。 如果Sun的编译器使行为保持不变,那么它不会从未定义的行为中解脱出来。

我认为答案取决于编译器。 该语言指定 :

在运行时,赋值expression式的结果是赋值发生后variables的值。

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

但是,使用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之前将其加载到ret 。 在这种情况下,存储在number的值在赋值到ret之前是否被修改并不重要,因为1 ,而不是赋值。

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

如果javac可以被信任,我们可以得出结论,该语句不涉及一个易失性的number读取。 赋值expression式x=y的值实际上只是y的值(转换后)。

我们也可以推断出这一点

  System.out.println(number=1); 

不涉及阅读number

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

不涉及阅读

  x_1=x_2=...x_n=v 

不涉及读取x_n, x_n-1, ... ; 相反, v的值直接赋值给x_i (经过x_n, ... x_itypes的必要转换之后)