3
class Q {
  volatile boolean valueSet = false;
  volatile int n;

  synchronized int get () {
    if ( !valueSet ) {
      try {
        wait();
      } catch ( InterruptedException e ) {
        System.out.println( "InterruptedException caught" );
      }
    }

    System.out.println( "Got: " + n );
    valueSet = false;
    notify();
    return n;
  }

  synchronized void put ( int n ) {
    if ( valueSet ) {
      try {
        wait();
      } catch ( InterruptedException e ) {
        System.out.println( "InterruptedException caught" );
      }
    }

    this.n = n;
    valueSet = true;
    System.out.println( "Put: " + n );
    notify();
  }
}

class Producer
    implements Runnable {
  Q q;
  Producer ( Q q ) {
    this.q = q;
    new Thread( this, "Producer" ).start();
  }

  public void run () {
    int i = 0;
    while ( true ) {
      q.put( i++ );
    }
  }

}

class Consumer
    implements Runnable {
  Q q;
  Consumer ( Q q ) {
    this.q = q;
    new Thread( this, "Consumer" ).start();
  }

  public void run () {
    while ( true ) {
      q.get();
    }
  }

}

class PCFixed {
  public static void main ( String args[] ) {
    Q q = new Q();
    new Producer( q );
    new Consumer( q );
    System.out.println( "Press Control-C to stop." );
  }
}

これがどのように機能するのか理解できません。例えばこんな流れ。プロデューサーは put メソッドに入り、notify() を呼び出します。wait() がまだ消費者によって呼び出されていない場合はどうなるでしょうか? また、プロデューサーが notify() を呼び出すと、プロデューサーがまだモニターを手放していないときに、コンシューマーはどのようにしてメソッド get() に入ることができますか? ここで私を助けてください。

4

3 に答える 3

2

最上部のクラスは Q であると想定していますが、一部のコードが欠落しています。とにかく、一般的な考え方は、ブール値valueSetwait()/notify()呼び出しが連携して機能するということです。

コンシューマーがすでに待機を開始している場合、同期get()メソッドを使用して Q インスタンスへのロックを取得し、待機中に解放します。

コンシューマーがまだ待機を開始していない場合、put()メソッドは同じロックで同期されるため、プロデューサーは Q インスタンスへのロックを保持している可能性があります。プロデューサーがロックを終了すると、呼び出しnotify() が行われ、valueSet ブール値が true に設定されます

消費者の次の呼び出しは、待機を試みる前にget()ブール値を読み取り、何かがあることに気づき、の内容を取得して、必要な作業を行います。値が設定されていない場合、つまり消費者が不在のときに何も入ってこなかった場合、消費者は新しい仕事のためにロックをかけ、次の仕事で目を覚まします。nwait()notify()

アップデート

コメントのシナリオでは、本質的にまったく同じ状況について質問していますが、逆です。同じ論理が適用されます。

コンシューマーは現在待機中で、プロデューサーは を呼び出しますnotify()。プロデューサは現在ロックを保持しており、メソッドの実行中はロックを保持し続けます。現在ロックを待機している別のnotify()スレッドに、ロックが解放されたときにロックを取得して実行を再開できることを知らせるだけです。この場合、他のスレッドは 1 つしかありませんが、複数のスレッドがある場合は、1 つだけを選択します (全員を起こすにnotifyAll()は、呼び出す必要があります)。

  1. プロデューサーはメソッドを終了し、ロックを解放します。
  2. コンシューマーが目を覚まし、ロックを取得します。

この時点で、プロデューサがすでに来て現在ロックを待っているのか、それともまだメソッドに入っていないのかはあいまいです。ブールフラグとwait()/の同じタンデムがnotify()これにも適用されます。

コンシューマーがメソッドを終了してロックを解放する前に、ブール値フラグを false に設定し、 を呼び出しますnotify()

プロデューサーが現在すでにロックを待機している場合、呼び出すnotify()と、ロックが解放されたときにウェイクアップして続行できることが通知されます。

プロデューサがwait()呼び出しを待機していない場合は、メソッドの外にある必要があります (メソッドに入ってロックを取得するのを待っている可能性があります)。コンシューマーがメソッドを終了してロックを解除すると、プロデューサーはそれを取得してブール フラグをチェックします。false に設定されているため、プロデューサーは呼び出しを試行せず wait()、その値を削除して、ブール値フラグを設定し、 を呼び出しますnotify()

于 2011-12-20T18:26:00.940 に答える
1
  1. wait() がまだ消費者によって呼び出されていない場合はどうなるでしょうか?
    • メッセージが失われます
  2. プロデューサーが notify() を呼び出したら、プロデューサーがまだモニターを手放していないときに、コンシューマーはどのようにしてメソッド get() に入ることができますか?
    • デッドロックします-モニターが解放されるまでブロックします。
于 2011-12-20T18:12:13.187 に答える
0

これがどのように機能するかを理解しようとします。たとえばProducer、メソッドに入りput()、モニターが空いていることを確認し、パッティング アクションを実行できるとします。彼がここで仕事をしているときに が来て、アクションConsumerを実行しようとします。get()しかし、モニターが によって既に使用されているため、彼は失敗しますProducerwait()つまり、他のスレッドが を呼び出すのを待っているコード行でブロックされていることを意味しますnotify()。ここでの秘訣はget()、モニターが再び解放されたことを通知されるまで、彼は二度と電話をかけないということです。つまり、彼はこれ以上空のサイクルを実行しません。それで、 がProducer仕事を終えると、彼は電話をかけnotify()て、Consumer彼が中断したところから再開します。最初は理解するのが少し難しく、説明するのも少し難しいことはわかっていますが、理解すると、これらが本当にシンプルで強力なものであることがわかります. 幸運を!

于 2011-12-20T18:19:33.473 に答える