3

同僚によると、JVM は、オブジェクトに対して「notify」を呼び出すときに、その時点で正しい「wait」が通知されることを保証していません。彼は、もはや有効ではない以前の通知が無効な時間に配信される場合があると述べています。

これは本当ですか?もしそうなら、これはどのように/なぜですか?また、これが機能するのと同じくらい基本的なものを想定できない場合、待機/通知メカニズムは何に使用されますか?

4

3 に答える 3

2

java.lang.Object.notifyの場合、Javadocは次のように述べています。

このオブジェクトのモニターで待機している単一のスレッドを起動します。このオブジェクトを待機しているスレッドがある場合は、そのうちの1つが起動するように選択されます。選択は任意であり、実装の裁量で行われます。スレッドは、waitメソッドの1つを呼び出すことにより、オブジェクトのモニターで待機します。

特定の状態を待つパターンは次のとおりです。

synchronized( lock ) {
   while( conditionEvaluation( data )) {
      lock.wait();
   }
}

カウンターパートはjava.lang.Object.notifyAll()、アプリケーションの活気を確保するために使用する必要があります。今日、それはたった1人のウェイターですが、ソフトウェアの多くの進化の後、将来的には数人のウェイターになる可能性があるため、notifyAll()よりも堅牢ですnotify()

于 2012-12-07T07:40:01.043 に答える
2

固有ロックを待機する各オブジェクトは、ロックの待機セットに入ります。ロック オブジェクトで通知を呼び出すと、待機セット内のスレッドの 1 つが選択され、作業が再開されます。JVM が提供する唯一の保証は、待機中のスレッドが最終的に通知しました。この非決定論的な動作の主な理由の 1 つは、中断されたスレッドが JVM によって実行のために選択される方法です。これは任意です。さらに、Java のロックは、スレッド バージングを許可する不公平なロック ポリシーを実装します。これは単に、要求時にロックが使用可能であれば、新しいロック要求がロックの待機セットを超えることが許容されることを意味します。この理由は、かなりの競合が発生した場合、待機セットで中断されたスレッドを選択して再開する前に、実際に実行されるまでに (潜在的に重大な) 遅延が発生する可能性があるためです。したがって、スレッドからの着信ロック要求は、再開されたスレッドの実行準備が整うまでにロックが解放されることを期待して、この時間遅延を利用してすぐに実行できます。

  1. 以前にモニター X を獲得したスレッド A は、notify() を呼び出します。
  2. モニター X で待機しているスレッド B が (任意に) 中断されることを選択しました。
  3. スレッド C はモニター X を取得しようとし、それが使用可能であることを確認して取得します。
  4. スレッド C が実行されます (スレッド B は現在再開中ですが)
  5. スレッド C は実行を終了し、スレッド B が実際に実行される直前にモニター X を解放します。
  6. スレッド B は実行する準備ができているため、ロックを取得して実行を開始します。

ステップ 2 と 6 の間に、実際にロックを使用しているスレッドがない時間間隔が存在することは明らかです。スレッド C が割り込んで、時間間隔を最適化として利用します。もちろん、これの欠点は、スレッド B の実行準備が整った時点でロックを解放しないというリスクです。その時点で、スレッド B はロックが使用できないことに気づき、再び待機セットに入ります。ただし、統計的には、不公平なロックの方がほとんどの状況でパフォーマンスが向上することが証明されています。

余談ですが、待機中のスレッドがロックを取得した順序で再開される公平なロックを使用できますが、実際にはパフォーマンスが低下します。これについて詳しくは、http: //docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/locks/ReentrantLock.htmlを参照してください。

これがあなたの質問に答えることを願っています。

于 2012-12-07T07:59:27.280 に答える
1

いいえ、それは真実ではありません。スレッドがnotifyを呼び出すと、待機中の1つのスレッドが起動されます(そのようなスレッドが存在する場合、それ以外の場合は通知が失われます)。おそらく、あなたの同僚は「スプリアス通知」を念頭に置いていました。これは、実際には他のスレッドが呼び出されていない場合notifyや、スレッドをスリープ解除できる場合がありますnotifyAll。「スプリアス通知」をフィルタリングするには、各notify呼び出しで監視対象オブジェクトの状態が変化し、待機中のスレッドがその状態を確認する必要があります。

 synchronized void up() {
    counter++;
    notify();
 }
 synchronized void down() {
   while (counter==0) {
      wait();
   }
   counter--;
 }

状態のチェックインdown()は、wait()を呼び出す前に行われることに注意してください。これは、呼び出しの前に状態が変更され、通知が失われる可能性があるためです。言い換えると、実際の情報はオブジェクトの状態とともに渡され、待機/通知はポーリングを回避するのに役立つだけです。オブジェクトの状態を変更せずに通知に依存しないでください。

于 2012-12-07T08:11:26.770 に答える