3

3 つのスレッドが 4 番目のスレッドを待機しており、4 番目のスレッドが通知を発行すると、待機中のすべてのスレッドが目覚めます。

ソースコードは次のとおりです。

class Reader extends Thread {

  Calculator calc;

  public Reader(Calculator calc) {
    this.calc = calc;
  }

  public void run() {
    synchronized(calc) {
      try {
        System.out.println(Thread.currentThread().getName() + " waiting for calc");
        calc.wait();
      } catch (InterruptedException e) { e.printStackTrace(); }
      System.out.println(Thread.currentThread().getName() + " Total is: " + calc.total);
    }   
  }

}

class Calculator extends Thread {

  public int total = 0;

  public void run() {
    synchronized(this) {
      for (int i = 0; i < 50; i++) {
        total += i;
      }
      notify();
      System.out.println("I notified a thread");
    }
  }

}

public class Notify {

  public static void main(String[] args) {
    Calculator calc = new Calculator();
    Reader r1 = new Reader(calc);
    Reader r2 = new Reader(calc);
    Reader r3 = new Reader(calc);
    r1.start();
    r2.start();
    r3.start();
    calc.start();
  }
}

これが私が得る出力です:

Thread-2 waiting for calc
Thread-4 waiting for calc
Thread-3 waiting for calc
I notified a thread
Thread-2 Total is: 1225
Thread-3 Total is: 1225
Thread-4 Total is: 1225

System.out.println(Thread.currentThread().getName() + " Total is: " + calc.total);待機中のスレッドを 1 つだけ起動して命令を実行するべきではありませんか?

4

5 に答える 5

3

wait/notifyこのように使用することはできません。あなたは何か、あなたのコードがテストできる何か、そしてテストしなければならない何かwaitを待つために使用しなければなりません。notify別のスレッドが実際に待機しているものを変更した後でのみ、そのテストで待機しないように指示するように呼び出す必要があります。

「スレッドは、通知、中断、タイムアウトなしでウェイクアップすることもできます。これは、いわゆるスプリアスウェイクアップです。これは実際にはめったに発生しませんが、アプリケーションは、スレッドが発生する原因となった状態をテストすることで、スレッドを防ぐ必要があります。目覚め、条件が満たされない場合は待ち続けます。」

つまり、waitロジックは次のようになっている必要があります。

  1. 今何かできますか?
  2. いいえの場合は、電話waitして手順1に進みます。
  3. そのことをしなさい。

そして、notifyロジックは次のようになっている必要があります。

  1. 別のスレッドが何かを実行できるようにします。
  2. を呼び出しnotifyます。

これらの機能は、一般的なサスペンド/レジュームメカニズムではありません。これらは特に、同期されたブロックによって保護されたコードによって管理される述語で同期する方法です。必要に応じて、独自の疑わしい/再開フラグを使用するか、必要に応じてカウントすることにより、それらから一時停止/再開メカニズムを構築できます。

更新:MånsRolandiDanielssonは、特定のケースで何が起こっているのかを理解しました。スレッドは、まだ起動/終了されていないオブジェクトを待機しています。したがって、準備ができている/終了していることを自分自身に通知すると、他のスレッドはその信号を確認します。

于 2012-11-06T18:54:54.543 に答える
3

電卓スレッドが notify() をまったく発行していない場合でも、すべてのスレッドが目覚めた理由がわかりました。これは、run() メソッドの実行が終了したためです。

notify() と notifyAll() の効果を確認するには、notify を呼び出した後も電卓を実行し続ける必要がありました。

したがって、このコード:

class Calculator extends Thread {

  int total;

  public void run() {
    synchronized(this) {
      for (int i = 0; i < 50; i++) {
        total += i;
      }
      notify();
      System.out.println("I notified a thread");
    }
    try {
     System.out.println("Going to sleep");
     Thread.sleep(5000);
     System.out.println("I finished sleeping");
    } catch(InterruptedException e) {}
  }

}

次の出力が得られます。

Thread-2 waiting for calc
Thread-4 waiting for calc
Thread-3 waiting for calc
I notified a thread
Thread-2 Total is: 1225
Going to sleep
I finished sleeping
Thread-3 Total is: 1225
Thread-4 Total is: 1225

これは、1 つの待機中のスレッドのみが notify() 呼び出しで通知され、他の 2 つのスレッドは電卓が実行を終了した後に目覚めたことを示しています。

前のコードで notify() を notifyAll() に置き換えると、次の出力が得られます。

Thread-2 waiting for calc
Thread-4 waiting for calc
Thread-3 waiting for calc
I notified all threads
Thread-3 Total is: 1225
Thread-4 Total is: 1225
Thread-2 Total is: 1225
Going to sleep
I finished sleeping
于 2012-11-06T20:01:49.543 に答える
0

Exploring Java (Niemeyer) でこの動作に関する行を見つけました:先入れ先出し方式でねじ込みます。

Java の探索

したがって、電卓にこの run() メソッドを使用すると、次のようになります。

public void run() {
    int k = 0;
    while (k++ < 2) {
        synchronized (this) {
            notify();
        }
    }
}

最初の 2 つの待機中のインスタンスに、計算機インスタンスで wait() メソッドを呼び出した順序で一度に 1 つずつ通知し、終了時に、この特定のインスタンスで待機している残りの Reader に通知する notifyAll を呼び出します。これが、この非常に興味深い議論で見られた行動の完全な説明であると私は信じています。

于 2012-11-07T09:01:59.207 に答える
0

いいえ、すべての Reader オブジェクトに同じ Calculator が与えられているためです。Reader スレッドが開始されると、同じ Calculator オブジェクトでcalc.wait() が呼び出されます。その後、Calculator が開始されて自身に通知されると、すべての Reader スレッドが一度に終了します。ほら!編集代わりに試してください:

public static void main(String[] args) throws InterruptedException,
  IOException {
  Calculator calc1 = new Calculator();
  Calculator calc2 = new Calculator();
  Calculator calc3 = new Calculator();
  Reader r1 = new Reader(calc1);
  Reader r2 = new Reader(calc2);
  Reader r3 = new Reader(calc3);
  r1.start();
  r2.start();
  r3.start();
  calc2.start();
 }

以下のテスト コードを試してみると、Calculator オブジェクトを共有している r1 と r2 が通知されて終了し、独自の Calculator を持つ r3 が待機していることがわかります。

public static void main(String[] args) throws InterruptedException,
  IOException {
  Calculator calc1 = new Calculator();
  Calculator calc2 = new Calculator();
  Reader r1 = new Reader(calc1);
  Reader r2 = new Reader(calc1);
  Reader r3 = new Reader(calc2);
  r1.start();
  r2.start();
  r3.start();
  calc1.start();
 }
于 2012-11-06T19:07:30.547 に答える