Возвращаемое значение оператора присваивания в параллельном коде


Дан следующий класс:

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 34

3 ответа:

В JLS 15.26 указано:

Существует 12 операторов присваивания; все они синтаксически правоассоциативны (группируются справа налево). Таким образом, a=b=c означает a=(b=c), который присваивает значение c b, а затем присваивает значение b a.

Ответ Теда Хоппа показывает, что javac Sun не следует этому поведению, возможно, в качестве оптимизации.

Из-за резьбы здесь поведение метода 1 будет неопределенным. Если компилятор Sun делает поведение константа тогда не вырывается из неопределенного поведения.

Я думаю, что ответ зависит от компилятора. Язык определяет:

Во время выполнения результатом выражения присваивания является значение переменной после выполнения присваивания.

Я предполагаю, что теоретически значение может быть изменено до того, как произойдет второе (самое левое) присвоение.

Однако, с компилятором javac Sun, 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 присваивается.

Либо оператор содержит летучее чтение, либо он не содержит летучее чтение. Здесь не может быть никакой двусмысленности, так как волатильное чтение очень важно для семантики программы.

Если 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