2

スレッドセーフを理解しようとしているので、このクラスがスレッドセーフかどうか知りたいですか?2つのスレッドがいつでもその変数にアクセスしようとすると、次の理由でスレッドセーフであるように見えます。

  • 最終変数は不変であるため、スレッドセーフです。
  • getterとsetterは同期されているため、mObjectは、一度に1つのスレッドでのみ取得または設定できます。したがって、2つのスレッドが異なる値を読み取る可能性はありません。
  • メソッドchangeObj()は同期されませんが、クラス変数(つまり、mObject)を処理するその中のブロックは同期されます。

私が間違っているのか、このクラスがスレッドセーフではないのか教えてください。

public class MyClass{

   private final String = "mystring"; //thread safe because final
   private AnObject mObject;

   public MyClass(){
       //initialize
   }

   //only one class can set the object at a time.
   public synchronized void setObj(AnObject a){
      mObject = a;
   }

    //two threads trying to get the same object will not read different values.
   public synchronized AnObject getObj(){
      return mObject;
   }

   //better to synchronize the smallest possible block than the whole method, for performance.
   public void changeObj(){
       //just using local variables (on stack) so no need to worry about thread safety
       int i = 1;
       //i and j are just to simulate some local variable manipulations.
       int j =3;
       j+=i;
       synchronized(this){
          //change values is NOT synchronized. Does this matter? I don't think so.
          mObject.changeValues();
      }
   }
}
4

4 に答える 4

6

いいえ、スレッドセーフではありません。AnObjectメソッドを使用する場合は、一度に1つのスレッドのみが値を変更できることを確認しますchangeObj()が、このオブジェクトのゲッターも提供するため、他のスレッドがchangeValues()同時に呼び出すことができます。

于 2012-10-10T18:41:13.970 に答える
2

クラス自体は現在の状態ではスレッドセーフです(ここに示されていないメソッドがあると仮定します)が、考えているところに「バグ」が1つある可能性があります。

mObject100%カプセル化されているわけではなく、セッターを介して渡され、ゲッターを介してフェッチできます。つまり、どのスレッドも、MyClassが認識していなくても、mObjectによって参照されるオブジェクトへの参照を取得し、そのオブジェクトのメソッドを同時に呼び出すことができます。

言い換えれば、AnObjectのメソッドもスレッドセーフである必要があるかもしれません。

少なくとも、MyClassで同期しても、mObjectがスレッドの問題から安全になるわけではありません

于 2012-10-10T18:43:58.180 に答える
1

JB Nizet氏の指摘に加えて、AnObject.changeValuesがクライアントによってオーバーライドされるように設計されたメソッドである場合、またはそのようなメソッドを呼び出す場合、これは一般的に、デッドロックやデータ破損などのさまざまな望ましくない動作への扉を開きます。同期されたブロック内のエイリアンコードに制御を委譲してはなりません。「エイリアン」とは、あなたの管理下にないコードを意味します。詳細については、Effective Java、第2版、アイテム67を参照してください。

于 2012-10-10T18:46:50.970 に答える
1

最終変数は必ずしもスレッドセーフではありません。不変の最終変数のみがスレッドセーフです。これは、のようなプリミティブやクラスString、またはそれ自体がスレッドセーフであるクラスの最終変数をカバーします。

クラスは変数を公開するためスレッドセーフではありませんaが、内部動作のためにも必要です。

a以下の例は、どのようにして一貫性のない状態になる可能性があるかの例を示しています。

スレッドA

MyClass myClass = ...
myClass.changeObj(); 
// imagine Thread A is suspended during the synchronized block inside of 
// changeObj()

Thead B

MyClass myClass = ...
AnObj anObj = myClass.getObj();
anObj.changeValues();
// uh-oh, I am modifying the state of this instance of anObj which is also 
// currently being modified by Thread A

MyClassを真にスレッドセーフにするには、次のいずれかを実行する必要があります。

  • AnObjは、スレッドセーフも保証する必要があります(状態をスレッドセーフに変更するメソッドを作成することにより)
  • AnObjは不変でなければなりません。つまり、AnObjの状態を変更する必要がある場合は、新しい状態を保持するためにAnObjの新しいインスタンスを作成する必要があります。
  • AnObjのゲッターは、MyClassのAnObjのインスタンスを直接公開するのではなく、そのインスタンスのコピーを返す必要があります。
于 2012-10-10T18:50:09.607 に答える