web-dev-qa-db-ja.com

並行コードでの代入演算子の戻り値

次のクラスがあるとします。

_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以外のものが返される可能性がありますか?

34
BeeOnRope

JLS 15.26は以下を指定します。

12個の代入演算子があります。すべて構文的に右結合性です(右から左にグループ化されます)。したがって、a = b = cはa =(b = c)を意味し、cの値をbに割り当ててから、bの値をaに割り当てます。

Ted Hoppの回答は、Sunのjavacが、おそらく最適化として、この動作に従わないことを示しています。

ここでのスレッド化により、method1の動作は未定義になります。 Sunのコンパイラが動作を一定にした場合、未定義の動作から逸脱することはありません。

10
Bringer128

答えはコンパイラ次第だと思います。言語 指定

実行時に、割り当て式の結果は、割り当てが発生した後の変数の値になります。

理論的には、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が割り当てられているためです。

15
Ted Hopp

ステートメントに揮発性読み取りが含まれているか、または揮発性読み取りが含まれていません。揮発性読み取りはプログラムのセマンティクスにとって非常に重要であるため、ここではあいまいさはありません。

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のタイプによる必要な変換後)

5
irreputable