41

職場の誰かが、同期の中で待機をラップする必要がある理由を尋ねました。

正直、理屈が見えません。スレッドがオブジェクトのモニターの所有者である必要があるという javadoc の内容は理解できますが、なぜですか? どのような問題を防ぎますか? (実際に必要な場合は、wait メソッドがモニター自体を取得できないのはなぜですか?)

かなり詳細な理由、または記事への参照を探しています。簡単なグーグルで見つけられませんでした。

ああ、また、thread.sleep はどのように比較されますか?

編集: すばらしい回答のセット - 何が起こっているのかを理解するのにすべての回答が役立ったので、複数選択できたらいいのにと思います。

4

6 に答える 6

14

ここにはすでにたくさんの良い答えがあります。しかし、ここで言いたいのは、wait() を使用するときにもう 1 つ行う必要があることは、私の経験では発生する偽のウェイクアップが発生した場合に備えて、待機している条件に応じてループで実行することです。

他のスレッドが条件を true に変更して通知するのを待つには:

synchronized(o) {
  while(! checkCondition()) {
    o.wait();
  }
}

もちろん、最近では、新しい Condition オブジェクトを使用することをお勧めします。より明確で、より多くの機能 (ロックごとに複数の条件を許可する、待機キューの長さを確認できる、より柔軟なスケジュール/割り込みなどを許可するなど) があるからです。

 Lock lock = new ReentrantLock();
 Condition condition = lock.newCondition();
 lock.lock();
 try {
   while (! checkCondition()) {
     condition.await();
   }
 } finally {
   lock.unlock();
 }

}

于 2008-10-22T18:58:45.637 に答える
6

Object.wait() を呼び出すときにオブジェクトがオブジェクト モニターを所有していない場合、モニターが解放されるまで、オブジェクトにアクセスして通知リスナーを設定することはできません。代わりに、同期されたオブジェクトのメソッドにアクセスしようとするスレッドとして扱われます。

または、別の言い方をすれば、以下の間に違いはありません。

public void doStuffOnThisObject()

そして次の方法:

public void wait()

オブジェクト モニターが解放されるまで、両方のメソッドがブロックされます。これは、オブジェクトの状態が複数のスレッドによって更新されるのを防ぐための Java の機能です。これは単に、wait() メソッドに意図しない結果をもたらすだけです。

おそらく、wait() メソッドは同期されていません。これは、スレッドがオブジェクトに対して複数のロックを持っている状況を作成する可能性があるためです。(詳細については、 Java 言語仕様/ロックを参照してください。) 複数のロックは問題になります。メソッドが同期されている場合、メソッドのロックのみが元に戻され、潜在的な外部ロックが元に戻されたままになることが保証されます。これにより、コードにデッドロック状態が発生します。

Thread.sleep() に関する質問に答えるために、Thread.sleep() は、待機している条件が満たされていることを保証しません。Object.wait() と Object.notify() を使用すると、プログラマは手動でブロッキングを実装できます。条件が満たされたという通知が送信されると、スレッドのブロックが解除されます。例: ディスクからの読み取りが完了し、データをスレッドで処理できます。Thread.sleep() では、条件が満たされた場合はプログラマーがポーリングし、満たされていない場合はスリープに戻る必要があります。

于 2008-10-22T16:03:09.530 に答える
5

wait() の目的はモニターを解放し、他のスレッドがモニターを取得して独自の処理を実行できるようにすることであるため、モニターを所有する必要があります。これらのメソッド (待機/通知) の目的は、何らかの機能を実行するために互いを必要とする 2 つのスレッド間で、同期されたコード ブロックへのアクセスを調整することです。データ構造へのアクセスがスレッドセーフであることを確認するだけではなく、複数のスレッド間でイベントを調整する必要があります。

古典的な例は、あるスレッドがデータをキューにプッシュし、別のスレッドがデータを消費するプロデューサー/コンシューマーのケースです。消費スレッドは常にモニターがキューにアクセスする必要がありますが、キューが空になるとモニターを解放します。プロデューサー スレッドは、コンシューマーが処理を行っていない場合にのみ、スレッドへの書き込みアクセスを取得します。さらにデータをキューにプッシュすると、消費者スレッドに通知されるため、モニターを取り戻してキューに再度アクセスできます。

于 2008-10-22T16:40:47.130 に答える
5

待機はモニターを放棄するため、放棄するにはそれが必要です。Notify にはモニターも必要です。

これを行う主な理由は、wait() から戻ったときにモニターがあることを確認するためです。通常、共有リソースを保護するために待機/通知プロトコルを使用しており、それを安全にしたい場合に使用します。待機が戻ったらタッチします。通知と同じです -- 通常、何かを変更してから notify() を呼び出します -- 監視を行い、変更を行い、notify() を呼び出します。

次のような関数を作成した場合:

public void synchWait() {
   syncronized { wait(); }
}

待機が返されたときにモニターを取得できませんでした。取得できても、次に取得できない可能性があります。

于 2008-10-22T16:46:07.400 に答える
3

これが、制限が実際に要件である理由についての私の理解です。これは、ミューテックスと条件変数を組み合わせてしばらく前に作成したC++モニターの実装に基づいています。

mutex + condition_variable = monitorシステムでは、待機呼び出しによって条件変数が待機状態に設定され、ミューテックスが解放されます。条件変数は共有状態であるため、待機したいスレッドと通知したいスレッドの間の競合状態を回避するためにロックする必要があります。状態をロックするためにさらに別のミューテックスを導入する代わりに、既存のミューテックスが使用されます。Javaでは、待機中のスレッドがモニターを所有している場合、ミューテックスは正しくロックされます。

于 2008-10-22T16:53:17.463 に答える
3

キューが空であるという条件がある場合、ほとんどの場合、待機が行われます。

If(queue is empty)
     queue.wait();

キューが空であると仮定しましょう。現在のスレッドがキューをチェックした後に横取りした場合、別のスレッドがキューにいくつかの要素を追加すると、現在のスレッドはそれを認識せず、待機状態になります。それは間違っている。したがって、次のようなものが必要です

Synchornized(queue)
{
   if(queue is empty)
          queue.wait();
}

ここで、自分自身を同期として待機させた場合を考えてみましょう。コメントの 1 つで既に述べたように、ロックを 1 つだけ解放します。つまり、上記のコードで wait() が同期された場合、1 つのロックのみが解放されます。現在のスレッドがキューのロックで待機することを意味します。

于 2009-07-23T05:33:16.420 に答える