2013-02-27 8 views
5

このarticleは、Javaの "synchronized"キーワードについて語ります。Javaスレッド "synchronized"

... 
    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に "同期" しません

()上記の太字 ラインを防ぎますか?

答えて

6

これは、チェック・アンド・アクトの競合状態の例です。

シナリオでは、次のように起こるかもしれません:

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

これは、2つのスレッドがfooをインクリメントすることを試みてきたが、それは一度だけインクリメントされるという効果を有することを意味します。

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

6

誰もがあなたと一緒にfooを取得していることを保証していないので、あなた以外の誰もfooを元に戻していないことを保証しているからです。 )(

()を取得し、あなたがセットを呼び出しますが、このと同等として完全にそのコードと考えることができます:

int temp = getFoo(); //safe method 
temp = temp+1; //not protected here - im not holding any locks ... 
setFoo(temp); //safe method 
4

つのスレッドができたので、両方の方法で​​キーワードは、それがスレッドセーフことはありません。 getFooにコールし、別のスレッドがgetFooと同じ結果が得られます。その後、それぞれが1つを追加してsetFooにコールし、最終結果はfooが2回ではなく1回だけ増分されることです。あなたの記事が指摘するように、これは競合状態です。

スレッドを安全にするには、読取りと書込みの両方を別々のgetおよびsetメソッドを使用せずに同じ同期ブロックにまとめなければなりません。

public synchronized void addFoo(int addend) 
{ 
    foo += addend; 
} 
+0

'それから、それぞれが1つ追加されてsetFooが呼び出され、最終結果はfooが2回ではなく1回だけインクリメントされるということです。 –

+0

それぞれのスレッドは 'foo'を同じ値に更新するためです。例えば、それぞれが値2を取得し、それぞれが1を加えて3を取得し、それぞれが値を3に設定します。 – rgettman

+0

私が正しく理解していれば、 setFooを呼び出すと、結果はfooが2回インクリメントされます** ** ' –

1

コード内のメイントラップは、がsetFooと呼ばれることがあります。現実getFoosetFooを呼び出す前に呼び出されるため間違っている

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

の 種類。ここではそれを示した例は次のとおりです。

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 

あなたはbarfoo前に呼び出された見ることができるように。だからあなたのケースでは、2つ(またはそれ以上の)スレッドがsetFooを呼び出す前に現在の値を返すgetFooを呼び出す可能性があります。この場合、彼らは両方とも同じ値を持つことになり、0を言うことができますし、彼らはsetFooを呼び出しますとき、彼らは両方とも1

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); 
+0

したがって、 myC.setFoo(1) 'が' println(myC.getFoo()) 'の前に呼び出される可能性がありますか? –

関連する問題