在并发代码中返回赋值运算符的值
鉴于以下课程:
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_i
types的必要转换之后)