2010-12-15 23 views
2

ObservableのnotifyObserversメソッドで、なぜ符号器はarrLocal = obs.toArray();を使用しますか? コーダーがベクターを直接反復処理しないのはなぜですか?おかげObservable snapshot observer vector

public void notifyObservers(Object arg) { 

    Object[] arrLocal; 

    synchronized (this) { 
     /* We don't want the Observer doing callbacks into 
     * arbitrary code while holding its own Monitor. 
     * The code where we extract each Observable from 
     * the Vector and store the state of the Observer 
     * needs synchronization, but notifying observers 
     * does not (should not). The worst result of any 
     * potential race-condition here is that: 
     * 1) a newly-added Observer will miss a 
     * notification in progress 
     * 2) a recently unregistered Observer will be 
     * wrongly notified when it doesn't care 
     */ 
     if (!changed) 
      return; 
     arrLocal = obs.toArray(); 
     clearChanged(); 
    } 

    for (int i = arrLocal.length-1; i>=0; i--) 
     ((Observer)arrLocal[i]).update(this, arg); 
} 

答えて

2

彼らは(彼らが実際に呼んでいるどのようなコードがわからない場合は特に)同時変更を避けたいが、同時に、あまりにも長い間同期ブロックに滞在ません。

オプションは、操作全体を同期させ、オブザーバに通知しながらベクトルを直接反復することです。コメントが指摘するように(「Observerが独自のMonitorを保持している間にコールバックを任意のコードにしないようにする」)、Observableは潜在的に長い間ロックされたままになります。

オプション2は、ベクトルの一貫性のあるコピーを取得するのに十分長い時間だけ同期させることです。 その後、彼らは自分のプライベートコピーを反復する前にロックを解放することができます。

更新:オブザーバがオブザーバのリストを更新した場合、それを同時に反復することは良い考えではないかもしれません。そのため、1つのスレッドのシナリオでもコピーが通知されるようです。

+0

Observerがコールバック内に別のリスナーを追加しようとする(またはリスナーとして自身を削除する)場合、オプション1はデッドロックを作成します。 – sje397

+1

私はスレッドがデッドロックすることはできません(Observersは同じスレッドで呼び出されます)。もちろん、スレッドが何らかの形で関与している場合は、それが起こる可能性があります。 – Thilo

+0

「Observerが別のリスナーを追加しようとした場合」でもオプション1はConcurrentModificationExceptionを引き起こす可能性があります。コピーはとにかく助言されます。 – Thilo

0

コーダーは、現在のオブザーバーを「スナップショット」するために「obs.toArray()」を使用しています。彼らは、ベクトルの周りを明白に同期させることなく、それらの下で変化する可能性のあるベクトルに対して反復処理を防止しようとしています。

1

このスレッドのその他の回答は、コピーの目的については正しいです。しかし、Java 5+にはすでに自動的にコピーを行う正しいデータ構造があると私は言うでしょう:java.util.concurrent.CopyOnWriteArrayList

+0

CopyOnWriteArrayListは頻繁に更新するために非常に重いことを除いて。私は、オブザーバーをあまり頻繁に追加しないので、それは問題ではないかもしれないと思います。 – Thilo

+0

@Thilo:実際、リスナーリストは 'CopyOnWriteArrayList'の通常の使用例です。 –

+0

その場合+1 ... – Thilo

0

このコードは、notifyObserversが呼び出し時に(または少なくともコピー時に)登録されたオブザーバーに正確に通知しなければならないという意味を実装しています。

このセマンティックが必要でない場合、作成者は、反復処理がスレッドセーフである限り、あなたが示唆するように、更新を呼び出すオブザーバーを単純に反復することができます。実際に、それは私がそれを書いた方法です。

このクラスの書き方については、私には分かりません。一見、通知は、このようなコードを書くように頼まれる:

ある
observable.setChanged(); 
observable.notifyObservers(obj1); 

は、通知は、最初の変更として観察できるマークまたは他notifyObserversは何もしません。また、notifyObserversへのコールは、その操作の一部としてclearChangedを呼び出します。

2つのスレッドが通知を行っている場合を考えてみましょう。これらの2つのコマンドはそのようなスレッド間でインターリーブすることができます。この場合

Thread 1: observable.setChanged(); 
Thread 2: observable.setChanged(); 
Thread 1: observable.notifyObservers(obj1); 
Thread 2: observable.notifyObservers(obj2); 

、予想通り、スレッド1作品によってnotifyObserversへの最初の呼び出しを。しかし、最初の呼び出しで変更されたフラグがクリアされたため、スレッド2によるnotifyObserversの2回目の呼び出しは何も行いません。従って、オブザーバーはobj2引数を見ることはありません。

これを避けるために私が見ることのできる唯一の解決方法は、setChangednotifyObserversへの呼び出しを同期させ、インターリーブされないようにすることです。これはもちろん、オブザーバが同期ブロック内に通知され、発生してはならないという作者の声明に違反することを意味します。このため、このクラスの使用をお勧めしません。

関連する問題