web-dev-qa-db-ja.com

ロック状態が待機する理由は、ロックを保持する必要があります

Java言語では、何らかの条件が満たされるのを待つ前に、ロックを取得する必要があります。

たとえば、int Java monitor lock:

synchronized(lock){
   System.out.println("before lock ...");
   lock.wait();
   System.out.println("after lock ...");
}

または同時実行ユーティリティ:

Lock lock = new ReentrantLock();
Condition cond = lock.newCondition();

lock.lock();
try{
     System.out.println("before condition ...");
     cond.await();
     System.out.println("after condition ...");
}catch(Exception e){
     e.printStackTrace();
}finally{
     lock.unlock();
}

では、なぜ私たちはロックを保持せずに待つことができないのですか?

他の言語は異なりますか、それともJavaだけですか?

Java-SPECの定義だけでなく、設計後に理由を説明していただければ幸いです。

20
Chinaxing

スレッドが待つ必要があるかもしれない何かがあると想像してください。おそらくあなたはキューを持っていて、スレッドはそれを処理できるようにキューに何かがあるまで待つ必要があります。キューはスレッドセーフである必要があるため、ロックで保護する必要があります。次のコードを書くことができます:

  1. ロックを取得します。
  2. キューが空かどうかを確認します。
  3. キューが空の場合は、何かがキューに配置されるのを待ちます。

おっと、それはうまくいきません。キューにロックを保持しているので、別のスレッドがキューに何かを配置するにはどうすればよいですか?もう一度試してみましょう:

  1. ロックを取得します。
  2. キューが空かどうかを確認します。
  3. キューが空の場合は、ロックを解除して、何かがキューに配置されるのを待ちます。

おっと、今でも問題があります。ロックを解除した後、キューに何かが配置されるのを待つ前に、何かがキューに配置された場合はどうなりますか?その場合、私たちはすでに起こったことを待っています。

この正確な問題を解決するための条件変数が存在します。これらには、このウィンドウを閉じるアトミックな「ロック解除して待機」操作があります。

したがって、ロックを保持する必要があります。そうしないと、すでに発生した何かを待っていないことを確認する方法がありません。別のスレッドが待機中に競合するのを防ぐために、ロックを保持する必要があります。

13
David Schwartz

さて、私たちは何を待っていますか?条件が満たされるのを待っています。別のスレッドが条件を真にしてから、待機中のスレッドに通知します。

待機に入る前に、条件が偽であることを確認する必要があります。このチェックと待機はアトミック、つまり同じロックの下にある必要があります。そうしないと、条件がすでに真であるときに待機に入ると、ウェイクアップしない可能性があります。

したがって、wait()を呼び出す前に、ロックがすでに取得されているのは必要です。

_synchronized(lock)
{
    if(!condition)
        lock.wait();
_

wait()が自動的かつサイレントにロックを取得すると、lotのバグが検出されなくなります。


wait()からウェイクアップしたら、条件を再度確認する必要があります。ここで条件が真になる必要があるという保証はありません(さまざまな理由で、誤ったウェイクアップ、タイムアウト、中断、複数のウェイター、複数の条件)

_synchronized(lock)
{
    if(!condition)
        lock.wait();
    if(!condition)   // check again
        ...
_

通常、条件がまだfalseの場合は、再度待機します。したがって、典型的なパターンは

_    while(!condition)
        lock.wait();
_

しかし、二度と待ちたくない場合もあります。


裸の待機/通知が理にかなっている合法的なユースケースはありますか?

_synchronized(lock){ lock.wait(); }
_

承知しました;アプリケーションは、明確に定義された動作で、裸の待機/通知で構成できます。これが望ましい振る舞いであるという議論をすることができます。これは、その動作に最適な実装です。

ただし、これは一般的な使用パターンではなく、API設計で説明する理由はありません。

8
ZhongYu

スレッドがシグナルの進行を単に待っていた場合、それを行うための他のメカニズムがあります。おそらく、スレッドが操作されるのを待っており、何らかの条件を満たすロックによって保護されている状態があります。その状態を適切に保護するには、状態を待機する前後にスレッドがロックを保持する必要があるため、ロックの取得を要求することは理にかなっています。

1
Tim Bender

条件 のドキュメントを参照してください。

条件は、オブジェクトの待機プールまたは待機セットのようなものであり、オブジェクト監視メソッド(wait、notify、notifyAll)の使用に置き換わるものです。条件により、ある状態条件が真になる可能性があることが別のスレッドから通知されるまで、あるスレッドが実行を一時停止(「待機」)できます。条件インスタンスは、オブジェクトモニターメソッドが待機または通知するために共有オブジェクトのロックを必要とするのと同じように、本質的にロックにバインドされます。したがって、条件でawait()を呼び出す前に、スレッドは条件の生成に使用されるLockオブジェクトをロックしている必要があります。 await()メソッドが呼び出されると、条件に関連付けられたロックが解放されます。

1
user2953113