3
public class ObjectA {

   private void foo() {
       MutableObject mo = new MutableObject();
       Runnable objectB = new ObjectB(mo);
       new Thread(objectB).start();
   }

}

public class ObjectB implements Runnable {

    private MutableObject mo;

    public ObjectB(MutableObject mo) {
        this.mo = mo;
    }

    public void run() {
       //read some field from mo
    }
}

上記のコードサンプルからわかるように、Runnableを実装し、別のスレッドで可変オブジェクトを使用するクラスに可変オブジェクトを渡します。ObjectA.foo()は、新しいスレッドの開始後も可変オブジェクトの状態を変更できるため、これは危険です。ここでスレッドセーフを確保するための好ましい方法は何ですか?ObjectBに渡すときにMutableObjectのコピーを作成する必要がありますか?可変オブジェクトは内部で適切な同期を保証する必要がありますか?私はこれまで何度もこれに遭遇しました。特に、多くのGUIアプリケーションでSwingWorkerを使用しようとしたときです。私は通常、不変のオブジェクト参照のみが別のスレッドでそれらを使用するクラスに渡されるように努めますが、これが難しい場合もあります。

4

4 に答える 4

3

これは難しい質問であり、残念ながら、答えは「状況によって異なります」です。クラスのスレッドセーフに関しては、次の3つの選択肢があります。

  1. 不変にすれば、心配する必要はありません。しかし、これはあなたが求めているものではありません。
  2. スレッドセーフにします。つまり、クライアントコードがオブジェクトを変更する同時スレッドについて心配する必要がないように、クラスの内部に十分な同時実行制御を提供します。
  3. スレッドセーフではなく、クライアントコードに何らかの外部同期を強制します。

基本的に、#2と#3のどちらを使用するかを尋ねています。別の開発者がクラスを使用していて、外部同期が必要であることを知らない場合が心配です。並行性の意図を文書化する方法としてJCIPアノテーション を使用するのが好きです。@ThreadSafe @Immutable @NotThreadSafe開発者はまだドキュメントを読む必要があるため、これは防弾ではありませんが、チームの全員がこれらのアノテーションを理解し、一貫して適用すれば、物事がより明確になります。

たとえば、クラスをスレッドセーフにしない場合は、クラスをAtomicReference明確にして同期を提供するために使用できます。

public class ObjectA {

  private void foo() {
     MutableObject mo = new MutableObject();
     Runnable objectB = new ObjectB(new AtomicReference<>( mo ) );
     new Thread(objectB).start();
  }
}

public class ObjectB implements Runnable {

  private AtomicReference<MutableObject> mo;

  public ObjectB(AtomicReference<MutableObject> mo) {
    this.mo = mo;
  }

  public void run() {
   //read some field from mo
   mo.get().readSomeField();
  }
}
于 2012-09-05T20:50:12.477 に答える
1

あなたはそれを複雑にしすぎていると思います。それが例(参照が保持されていないローカル変数)である場合、誰もそれに書き込もうとしないことを信頼する必要があります。可能であれば、より複雑な(A.foo()LOCが多い)場合は、スレッドに渡すためだけに作成します。

new Thread(new MutableObject()).start();

そうでない場合(初期化のため)、ブロックで宣言して、別のプライベートメソッドであっても、すぐにスコープから外れるようにします。

{
   MutableObject mo = new MutableObject();    
   Runnable objectB = new ObjectB(mo);    
   new Thread(objectB).start();    
}
....
于 2012-09-05T20:24:12.577 に答える
1

オブジェクトをコピーします。コピーを新しいスレッドに渡すので、奇妙な可視性の問題は発生しません。Thread.startは常に、新しいスレッドがrunメソッドに入る前に発生します。このコードを変更してオブジェクトを既存のスレッドに渡す場合は、適切な同期が必要です。Java.util.concurrentからのブロッキングキューをお勧めします。

于 2012-09-05T20:29:26.743 に答える
1

あなたの正確な状況を知らなければ、この質問に正確に答えることは難しいでしょう。MutableObject答えは、何を表すか、同時に変更できる他のスレッドの数、およびオブジェクトを読み取るスレッドが、オブジェクトの読み取り中に状態が変化するかどうかを気にするかどうかに完全に依存します。

スレッドセーフに関しては、すべての読み取りと書き込みを内部的に同期することMutableObjectは、おそらく「最も安全な」ことですが、パフォーマンスが犠牲になります。読み取りと書き込みで競合が非常に多い場合は、プログラムでパフォーマンスの問題が発生する可能性があります。相互排除の保証をいくつか犠牲にすることで、パフォーマンスを向上させることができます。これらの犠牲がパフォーマンスの向上に値するかどうかは、解決しようとしている特定の問題に完全に依存します。

また、最終的に「内部同期」を行う方法でゲームをプレイすることもできますMutableObject。まだ読んでいない場合は、との違いを読んで、volatileさまざまsynchronizedな状況でスレッドセーフを確保するためにそれぞれをどのように使用できるかを理解することをお勧めします。

于 2012-09-05T20:29:31.333 に答える