誰でもスレッドモニターを説明して待つことができますか?
-
03-07-2019 - |
質問
職場の誰かが、同期内で待機をラップしなければならない背後にある理由を尋ねたところです。
正直なところ、私には理由がわかりません。私はjavadocsが言っていることを理解しています-スレッドはオブジェクトのモニターの所有者である必要があるが、なぜですか?どのような問題を防ぎますか? (そして実際に必要な場合、なぜwaitメソッドはモニター自体を取得できないのですか?)
私はかなり詳細な理由を探しているか、記事への参照を探しています。クイックGoogleで見つけることができませんでした。
また、thread.sleepはどのように比較されますか?
編集:一連のすばらしい回答-何が起こっているのかを理解するのに役立ったので、複数選択できることを本当に望みます。
解決
オブジェクトがObject.wait()を呼び出すときにオブジェクトモニターを所有していない場合、モニターが解放されるまでオブジェクトにアクセスして通知リスナーを設定することはできません。代わりに、同期オブジェクトのメソッドにアクセスしようとするスレッドとして扱われます。
または別の言い方をすると、次の間に違いはありません:
public void doStuffOnThisObject()
および次の方法:
public void wait()
両方のメソッドは、オブジェクトモニターが解放されるまでブロックされます。これは、オブジェクトの状態が複数のスレッドによって更新されるのを防ぐJavaの機能です。 wait()メソッドに意図しない結果が生じるだけです。
おそらく、wait()メソッドは同期されません。これは、スレッドがオブジェクトに複数のロックを持っている状況を作り出す可能性があるためです。 ( Java言語仕様/ロックを参照してください。詳細はこちら。)wait()メソッドは1つのロックのみを取り消すため、複数のロックは問題です。メソッドが同期されている場合、潜在的な外部ロックを元に戻したまま、メソッドのロックのみを元に戻すことが保証されます。これにより、コードにデッドロック状態が発生します。
Thread.sleep()に関する質問に答えるために、Thread.sleep()は、待機中の条件が満たされていることを保証しません。 Object.wait()およびObject.notify()を使用すると、プログラマーは手動でブロッキングを実装できます。スレッドは、条件が満たされたという通知が送信されるとブロックを解除します。例えばディスクからの読み取りが完了し、データをスレッドで処理できます。 Thread.sleep()は、条件が満たされている場合はプログラマにポーリングを要求し、満たされていない場合はスリープに戻ります。
他のヒント
ここにはすでにたくさんの良い答えがあります。しかし、ここで言及したいのは、wait()を使用するとき、他の人がやらなければならないことは、私の経験では発生している偽のウェイクアップが見られる場合に待機している条件に応じてループでそれを行うことです。
他のスレッドが条件を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();
}
}
wait()の目的はモニターを解放し、他のスレッドがモニターを取得して独自の処理を行うことであるため、モニターを所有する必要があります。これらのメソッド(待機/通知)の目的は、いくつかの機能を実行するために互いに必要な2つのスレッド間で同期されたコードブロックへのアクセスを調整することです。データ構造へのアクセスがスレッドセーフであることを確認するだけでなく、複数のスレッド間でイベントを調整することも重要です。
典型的な例は、あるスレッドがデータをキューにプッシュし、別のスレッドがデータを消費するプロデューサー/コンシューマーの場合です。消費スレッドは常にモニターがキューにアクセスする必要がありますが、キューが空になるとモニターを解放します。プロデューサースレッドは、コンシューマーが処理しなくなったときにのみスレッドへの書き込みアクセスを取得します。より多くのデータをキューにプッシュすると、コンシューマスレッドに通知するため、モニターを取り戻し、キューに再度アクセスできます。
Waitはモニターを放棄します。そのため、モニターを放棄する必要があります。 Notifyにもモニターが必要です。
これを行う主な理由は、wait()から戻ったときにモニターがあることを確認することです。通常、一部の共有リソースを保護するためにwait / notifyプロトコルを使用していて、待機が戻ったら、安全に触れてください。 notifyの場合も同じです-通常は何かを変更してからnotify()を呼び出しています-モニターを持ち、変更を加え、notify()を呼び出します。
次のような関数を作成した場合:
public void synchWait() {
syncronized { wait(); }
}
待機が戻ったとき、モニターはありません。取得することはできますが、次に取得できない場合があります。
制限が実際に要件である理由についての私の理解です。これは、ミューテックスと条件変数を組み合わせてしばらく前に作成したC ++モニターの実装に基づいています。
mutex + condition_variable = monitor システムでは、 wait 呼び出しは、条件変数を待機状態に設定し、ミューテックスを解放します。条件変数は共有状態であるため、待機するスレッドと通知するスレッド間の競合状態を避けるためにロックする必要があります。状態をロックするためにさらに別のミューテックスを導入する代わりに、既存のミューテックスが使用されます。 Javaでは、待機するスレッドがモニターを所有している場合、mutexは正しくロックされます。
キューが空であるという条件がある場合、ほとんどの待機が行われます。
If(queue is empty)
queue.wait();
キューが空であると仮定しましょう。 キューをチェックした後に現在のスレッドが横取りする場合、別のスレッドが スレッドはキューにいくつかの要素を追加しますが、現在のスレッドはそれを知らず、待機します 状態。それは間違っている。 したがって、次のようなものが必要です
Synchornized(queue)
{
if(queue is empty)
queue.wait();
}
ここで、待機自体を同期化した場合を考えてみましょう。コメントの1つで既に述べたように、1つのロックのみを解放します。つまり、上記のコードでwait()が同期された場合、ロックが1つだけ解放されていたことになります。現在のスレッドがキューをロックして待機することを意味します。