2016-05-13 5 views
2

コードのスレッドセーフについては全体的にはかなり良いですが、それを処理する最善の方法では明確ではありません。Javaスレッドセーフティ:メソッドを呼び出すときにインスタンス変数を処理する方法

私は、囲んでいるクラスがその値を変更できるので、最終的なものではないインスタンス変数を持っています。この変数は、アプリケーション中に呼び出されるいくつかのメソッドを持つオブジェクトです。ここに、私が意味することを示すための簡単なサンプルがあります。

private class Foo{ 

     private FooFoo fooFoo; 

     public synchronized void setFooFoo(FooFoo fooFoo){ 
      this.fooFoo = fooFoo; 
     } 

     public void doSomething(){ 
      fooFoo.doSomething(); //How do I make this line thread-safe? 
     } 

    } 

fooFooフィールドの参照を変更するのは簡単ですが、単純な同期だけです。しかし、doSomething()メソッドがfooFooで呼び出されたときはどうですか?デッドロックのリスクのために私は常に外国人の方法で同期することを躊躇しています。

これは実際にはこれに基づいていますが、これにはさまざまなバリエーションがあります。私の会社のアプリケーションは、しばしばスパゲッティのボウルに似ている大規模なコードベースであるため、あらゆる種類の同期コードを書くことになると、私は余分な編集的です。米国では、東ヨーロッパのオフショア企業で働いており、私は彼らのすべてが良いコーディングの決定を下すとは信じていません。

このような状況をマルチスレッド環境で処理するベストプラクティスを探しています。ありがとう。

+1

なぜそれが「デッドロックリスク」だと思いますか? –

+0

なぜ、setFooFoo()で十分である場合、doSomething()メソッドのシンプルな同期がうまくいかないと思いますか?両方のメソッドにsynchronizedキーワードを設定している場合、ロックは同じオブジェクト上にあるため、デッドロックの可能性はありません。 – Vijay

+1

3つのスレッドがある場合、スレッドAとスレッドBが同時に 'setFooFoo'を呼び出し、スレッド' C'が 'fooFoo.doSomething()'を呼び出す場合に何をしたいでしょうか?当時。シンクロナイズしなくても、このシナリオはクラッシュすることはありません。 – OldCurmudgeon

答えて

0

doSomething()​​を作成する必要があります。これは、クラスの可変部分に対する同時アクセスが発生するたびにロックする必要があるためです。 fooFoodoSomethingで読むのと同時に、fooFoosetFooFoo()に書き込んで、データレースを作成することもできます。​​は、本質的に、関数呼び出しが、エントリのJavaオブジェクトに関連付けられているロックを取得し、その関数を終了すると解放するようにします。

また、LockのメンバーをFooの中に置くこともできます。これは、他のメンバーが変更されている間に安全にアクセスできる複数の独立したメンバーを持つ場合に適用されます。この場合、2つの異なるロックを取るとコードが大幅に高速化する可能性があります。

完全性のために、​​メソッドでオブジェクトの本来のロックを取っている古いJavaのバージョン(Java 5のみ)では、ロックオブジェクトを使用するよりもかなり遅いことに注意してください。

デッドロックの問題については、これを心配するのは当然ですが、最初にオブジェクトをスレッドセーフにした後は別の問題と見なしてください。質問には、他にロックがかけられているものがあります。投稿したものだけでこれに答えることはできません。あなたのオブジェクトが与えられた場合、そのオブジェクトの読み込み/書き込みの同期は、その時に1つの操作だけがアクティブになり、コードに表示されているものから他のロックを取らないため、デッドロックできません。

0

スレッドの安全性に関する懸念事項によって異なります。

fooが委任されている場合は、単に揮発性にすることができます。これにより、参照が更新された場合にスレッドが古い値への参照をキャッシュすることを防ぎます。 FooFooはそれ自身のスレッドの安全性の問題を処理できます。

private class Foo{ 

    private volatile FooFoo fooFoo; 

    public void setFooFoo(FooFoo fooFoo){ 
     this.fooFoo = fooFoo; 
    } 

    public void doSomething(){ 
     fooFoo.doSomething(); 
    } 

} 

あなたの懸念がfoo自体のスレッドの安全性についてです、そして、それはちょうどあなたが関連するメソッドを同期化する必要があります呼び出しを委譲する、より多くのをやっている場合。

private class Foo{ 

    private FooFoo fooFoo; 

    public synchronized void setFooFoo(FooFoo fooFoo){ 
     this.fooFoo = fooFoo; 
    } 

    public synchronized void doSomething(){ 
     fooFoo.doSomething(); 
    } 

    public synchronized void doSomethingElse() { 
     int values = fooFoo.getValue(); 
     // do some things 
     fooFoo.setValue(values + somethingElse); 
    } 
} 
+0

2番目のサンプルの他のメソッドを同期するのを忘れています。現時点では、後者は必要な視認性保証を提供しません。 – Voo

+0

良いキャッチ、ありがとう、私は更新されます。 – cyroxis

2

fooFoo.doSomething()。 //この行をスレッドセーフにするにはどうすればよいですか?

ヒント:オブジェクトにアクセスするプログラム全体で唯一の行でない限り、その1行をスレッドセーフにすることはできません。

スレッドセーフは、特定のコード行または特定のメソッドをスレッドセーフにすることに関するものではありません。データスレッドセーフです。

fooFooは可変オブジェクトを参照していますか?そうでなければ、その行は既にスレッドセーフです。しかし、オブジェクトである場合、スレッド安全性は少なくとも2つ以上のスレッド間の意図しない相互作用がそのオブジェクトを無効な状態にすることを保証することを意味します。最悪の場合は、fooFooオブジェクトとプログラム内の他のオブジェクトとの間の関係の一貫性を保証することを意味します。

スレッド間で共有される2つ以上のデータの間に重要な関係がある場合は、一時的にその関係に違反する可能性のあるビットの周りに​​ブロックを投げる必要があり、​​ブロックのいずれかのビットの周りのは、その関係にが依存します---たとえコードがであっても、のデータを見ます。

関連する問題