次のクラスがあるとします。
_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が、おそらく最適化として、この動作に従わないことを示しています。
ここでのスレッド化により、method1の動作は未定義になります。 Sunのコンパイラが動作を一定にした場合、未定義の動作から逸脱することはありません。
答えはコンパイラ次第だと思います。言語 指定 :
実行時に、割り当て式の結果は、割り当てが発生した後の変数の値になります。
理論的には、2番目(左端)の割り当てが発生する前に値を変更できると思います。
ただし、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
に割り当てられる前に変更されても問題ありません。number
ではなく1
が割り当てられているためです。
ステートメントに揮発性読み取りが含まれているか、または揮発性読み取りが含まれていません。揮発性読み取りはプログラムのセマンティクスにとって非常に重要であるため、ここではあいまいさはありません。
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
のタイプによる必要な変換後)