6

この記事では、Java の「同期」キーワードについて説明します。

  ...
  private int foo;
  public synchronized int getFoo() { return foo; } 
  public synchronized void setFoo(int f) { foo = f; }

呼び出し元が foo プロパティをインクリメントしたい場合、次のコードはスレッドセーフではありません:

  ...
  setFoo(getFoo() + 1);

2 つのスレッドが同時に foo をインクリメントしようとすると、タイミングに応じて foo の値が 1 つまたは 2 つ増加する可能性があります。

さて、私の質問:

setFoo() の「同期」が上記の太字の行を妨げないのはなぜですか?

4

6 に答える 6

6

これは、check-then-act 競合状態の例です。

次のようなシナリオが発生する可能性があります。

Thread-1 getFoo() returns 0
Thread-2 getFoo() returns 0
Thread-2 setFoo(1)
Thread-1 setFoo(1)

これは、2 つのスレッドが foo をインクリメントしようとしたが、1 回しかインクリメントされないという効果があることを意味します。

他の回答が特定したように、スレッドは上記のようにインターリーブできないため、getFoo() および setFoo() と同じオブジェクトをロックする同期ブロックとインクリメントを同期させると、この競合状態が防止されます。

于 2013-02-27T17:53:50.060 に答える
6

他の誰もあなたと一緒に foo を取得していないこと、および他の誰も foo を設定し直していないことは保証されていますが、あなたが get() を呼び出してからあなたが設定()

そのコードは、これと完全に同等であると考えることができます。

int temp = getFoo(); //safe method
temp = temp+1; //not protected here - im not holding any locks ...
setFoo(temp); //safe method
于 2013-02-27T17:52:29.420 に答える
4

両方のメソッドのsynchronizedキーワードはスレッド セーフにはなりません。あるスレッドが を呼び出しgetFoo、次に別のスレッドが を呼び出しgetFoo、それぞれが同じ結果を得る可能性があるためです。次に、それぞれが 1 を追加して を呼び出しますsetFoo。最終結果はfoo、2 回ではなく 1 回だけインクリメントされます。あなたの記事が指摘しているように、これは競合状態です。

スレッド セーフにするには、get メソッドと set メソッドを分けずに、読み取りと書き込みの両方を同じ同期ブロックに含める必要があります。

public synchronized void addFoo(int addend)
{
   foo += addend;
}
于 2013-02-27T17:52:42.280 に答える
1

コードの主なトラップは、getFoo「内部」と呼ばれるように見えることですsetFoo。すこし

setFoo(){
   //getFoo();
   //...
}

実際getFooには を呼び出す前に呼び出されるため、これは正しくありませんsetFoo。これを示す例を次に示します。

public static int foo(int i) {
    System.out.print("FOO!");
    return i;
}

public static int bar(int i) {
    System.out.print("BAR!");
    return i;
}

public static void main(String[] args) throws Exception {
    System.out.println(foo(bar(1)));
}

出力:

BAR!FOO!1

ご覧のとおり、bar以前に呼び出されましたfoo。したがって、あなたの場合、2 つ (またはそれ以上) のスレッドが呼び出さgetFooれ、それらが呼び出される前に現在の値が返される可能性がありますsetFoo。この場合、それらは両方とも同じ値を持ち、0 としましょう。それらが呼び出さsetFooれると、両方とも 1 に設定されます。

于 2013-02-27T18:16:57.840 に答える
0

使えない

   private volatile int foo;

またはアトミックhttp://docs.oracle.com/javase/tutorial/essential/concurrency/atomicvars.html

于 2013-02-27T18:18:10.970 に答える
0

このコードは役に立ちますか?

class C {
  private int foo;
  public int getFoo() { return foo; } 
  public void setFoo(int f) { foo = f; }
}

C myC = new C();
synchronized(myC) {
  int foo = myC.getFoo();
  myC.setFoo(foo + 1);
}
println(myC.foo);
于 2013-02-27T18:54:30.813 に答える