71

従来の待機通知メカニズムに比べて、Conditionインターフェース/実装を使用する利点は何ですか?ここで、ダグ・リーが書いたコメントを引用します。

条件は、オブジェクトモニターメソッド(wait、notify、notifyAll)を個別のオブジェクトに分解し、任意のLock実装を使用してそれらを組み合わせることにより、オブジェクトごとに複数の待機セットを持つ効果を与えます。Lockが同期されたメソッドとステートメントの使用を置き換える場合、ConditionはObjectmonitorメソッドの使用を置き換えます。

これは、待機/通知メカニズムを実装するためのよりオブジェクト指向の方法だと思います。しかし、前者に比べて健全な利点はありますか?

4

5 に答える 5

37

最大の問題は、待機/通知が新しい開発者にとってエラーが発生しやすいことです。主な問題は、それらを正しく処理する方法がわからないことです。結果として、あいまいなバグが発生する可能性があります。

  • wait()の前にnotify()を呼び出すと、それは失われます。
  • notify()とwait()が同じオブジェクトで呼び出されているかどうかが不明確な場合があります。
  • 状態変更を必要とする待機/通知には何もありませんが、ほとんどの場合、これは必要です。
  • wait()は誤って戻る可能性があります

Conditionは、この機能を専用のコンポーネントにまとめますが、動作はほとんど同じです。

これより数分前に投稿されたwait/nofityに関する質問があり、さらに多くの検索[java] + wait + notify

于 2012-05-01T09:12:02.797 に答える
35

を使用Condition: await()/signal()すると、特定のシグナルを取得するオブジェクトまたはオブジェクト/スレッドのグループを区別できます。isEmptyこれは、プロデューサーである一部のスレッドがシグナルを取得し、コンシューマーがシグナルを取得する短い例ですisFull

private volatile boolean usedData = true;//mutex for data
private final Lock lock = new ReentrantLock();
private final Condition isEmpty = lock.newCondition();
private final Condition isFull = lock.newCondition();

public void setData(int data) throws InterruptedException {
    lock.lock();
    try {
        while(!usedData) {//wait for data to be used
            isEmpty.await();
        }
        this.data = data;
        isFull.signal();//broadcast that the data is now full.
        usedData = false;//tell others I created new data.          
    }finally {
        lock.unlock();//interrupt or not, release lock
    }       
}

public void getData() throws InterruptedException{
    lock.lock();
    try {
        while(usedData) {//usedData is lingo for empty
            isFull.await();
        }
        isEmpty.signal();//tell the producers to produce some more.
        usedData = true;//tell others I have used the data.
    }finally {//interrupted or not, always release lock
        lock.unlock();
    }       
}
于 2012-05-02T04:10:21.240 に答える
24

条件インターフェースについては、上記のような多くの利点があります。いくつかの重要な点は次のとおりです。

条件インターフェースには、次の2つの追加メソッドが付属しています。

1)boolean awaitUntil(Date deadline)throws InterruptedException: 現在のスレッドが通知または中断されるか、指定された期限が経過するまで待機します。

2)awaitUninterruptibly(): 現在のスレッドがシグナリングされるまで待機します。

現在のスレッドの中断ステータスがこのメソッドに入るときに設定されている場合、または待機中に中断された場合、通知されるまで待機し続けます。最終的にこのメソッドから戻ったとき、中断されたステータスは引き続き設定されます。

上記の2つのメソッドは、オブジェクトクラスにあるデフォルトのモニターには存在しません。状況によっては、スレッドが待機する期限を設定したい場合は、Conditionインターフェイスでそれを行うことができます。

状況によっては、スレッドを中断したくない場合や、現在のスレッドをシグナルが送信されるまで待機させたい場合は、ConditionInterfaceにあるawaitUninterruptiblyメソッドを使用できます。

詳細については、Condition Interface Javaドキュメント:

http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/locks/Condition.html#awaitUntil%28java.util.Date%29

于 2012-10-03T12:19:43.020 に答える
4

複数のウェイトセットを持つことが利点である理由を具体的に説明するには、次のようにします。

スレッドが待機しているさまざまなものがあるかどうかを待機/通知します(一般的な例は、固定サイズのブロックキューで、一部のスレッドはキューに入れてキューがいっぱいになるとブロックし、他のスレッドはキューから取得してブロックしますキューが空の場合)、notifyを使用して、スケジューラーが待機セットから1つのスレッドを選択して通知する場合、選択したスレッドが特定の状況について通知されることに関心がないというコーナーケースが発生する可能性があります。たとえば、キュ​​ーはキューに何かを追加するように通知しますが、選択したスレッドがプロデューサーであり、キューがいっぱいの場合、その通知に基づいて動作することはできません。組み込みロックでは、通知が失われないようにするために、notifyAllを使用する必要があります。

ただし、notifyAllはすべての呼び出しでチャーンを発生させ、すべてのスレッドがウェイクアップしてロックを争いますが、進行できるのは1つだけです。他のスレッドはすべて、一度に1つずつロックを取得して待機に戻るまで、ロックを争ってぶつかります。あまりメリットがないために多くの競合が発生します。通知を使用して、通知がそのスレッドに関連する1つのスレッドのみが通知されることを認識できることが望ましいでしょう。

これは、待機する個別の条件を持つことが大きな改善であるところです。キューは条件でシグナルを呼び出すことができ、そのスレッドが特に条件を待機している1つのスレッドのみをウェイクアップすることを認識できます。

ConditionのAPIドキュメントには、制限付きバッファーに複数の条件を使用することを示すコード例があります。

アイテムまたはスペースがバッファーで使用可能になったときに一度に1つのスレッドにのみ通知するという最適化を使用できるように、putスレッドを待機し続け、スレッドを別々の待機セットに入れたいと考えています。

于 2017-04-21T15:27:50.633 に答える
0

他のよく受け入れられている回答に加えて、ConditionはLockオブジェクトに関連付けられているため、クラスに任意のLockオブジェクトのセット(再書き込み、読み取り、書き込み)を設定し、それに特定の条件を関連付けることができます。次に、これらの条件のセットを使用して、実装セマンティクスに従ってクラスのさまざまな部分を同期できます。これにより、柔軟性と明示的な動作が得られ、待機してimoに通知します。

于 2020-02-23T19:29:17.940 に答える