鉴于以下课程:
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()
实例上同时调用method2()
和Foo
的多个线程,对method1()的调用是否可以返回除1以外的任何内容?
答案 0 :(得分:15)
我认为答案取决于编译器。语言specifies:
在运行时,赋值表达式的结果是赋值发生后变量的值。
我认为理论上可以在第二次(最左边)分配之前改变该值。
但是,使用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
,而不是number
。
答案 1 :(得分:10)
JLS 15.26规定:
有12个赋值运算符;所有这些都是语法上的右关联(他们从右到左分组)。因此,a = b = c表示a =(b = c),它将c的值赋给b,然后将b的值赋给a。
Ted Hopp的回答显示,Sun的javac不遵循这种行为,可能是一种优化。
由于此处的线程,方法1的行为将是未定义的。如果Sun的编译器使行为保持不变,那么它就不会破坏未定义的行为。
答案 2 :(得分:5)
该语句包含易失性读取,或者它不包含易失性读取。这里不存在任何歧义,因为易失性读取对于编程语义非常重要。
如果可以信任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