Возвращаемое значение оператора присваивания в параллельном коде
Дан следующий класс:
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 ответа:
В JLS 15.26 указано:
Ответ Теда Хоппа показывает, что javac Sun не следует этому поведению, возможно, в качестве оптимизации.Существует 12 операторов присваивания; все они синтаксически правоассоциативны (группируются справа налево). Таким образом, a=b=c означает a=(b=c), который присваивает значение c b, а затем присваивает значение b a.
Из-за резьбы здесь поведение метода 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