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

时间:2012-10-12 00:39:56

标签: java concurrency variable-assignment jls

鉴于以下课程:

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以外的任何内容?

3 个答案:

答案 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

类型进行必要的转换后