1

こんにちは私はここに提示されているこのコードを理解するのに問題があります。このコードは、wait()とnotify()をスレッドに正しく実装する方法の例を示しています。

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

class Q {

  int n;    
  boolean valueSet = false;

  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.");
    }
  }

ここでブール値の使用法を理解するのに苦労しています。ブール値変数が残っている場合、コードは正しく出力されます。

ただし、ブール値を削除すると、「Control-Cを押して停止」のみが出力されます。何故ですか?

ここでブール値が非常に重要なのはなぜですか。また、その使用法は何ですか。

ありがとう。

4

3 に答える 3

1

最後の値がまだ処理されていない場合、待機を避けるためにブール値が使用されているようです。たとえば、put メソッドでは、valueSet が true の場合、wait() をスキップしています。これは、最後に this.n の値を更新してから get() がまだ実行されていないことを意味するためです。ブール値に関係なく毎回 wait() すると、デッドロックが発生する可能性が高くなり、両方のスレッドが待機しており、どちらも通知しません。

これが、メソッドに synchronized キーワードを適用したくない理由です。どのオブジェクトがミューテックスとして使用されているかについて混乱する可能性があります。どのリソースが待機されているかがより明確になるため、私はこのスタイルを好みます。また、このスタイルは、同期する必要のないミューテックスの下で作業を行うことにより、怠惰を思いとどまらせることもわかりました。ただし、それはすべて個人的な好みです。

void get(int n){
    synchronized(this){
        // do the work
    }
}

void put(int n){
    synchronized(this){
        // do the work
    }
}
于 2013-02-27T00:19:47.757 に答える
1

ブール値の名前はvalueSet. true の場合、値が設定されていることを意味します。false の場合は、値が設定されていないことを意味します。ブール値をフラグと考えてください。true の場合は消費されるデータがあり (フラグが立っている)、false の場合は消費されるデータはありません (フラグが下がっています)。

プロデューサー スレッドは、フラグが false の場合にのみ値を設定します。true の場合、消費者からの通知を待ちます。

コンシューマー スレッドは、フラグが true の場合にのみ値を読み取ります。false の場合は、プロデューサーからの通知を待ちます。

デバッガーへのアクセスと使用経験はありますか? 2 つのスレッドをステップ実行して、それらが互いにどのように相互作用するかを確認すると、役立つ場合があります。以前にデバッガーを使用したことがない場合、マルチスレッドは理想的な学習シナリオではない可能性があります。

于 2013-02-27T00:15:53.720 に答える
1

このクラスQは、 に格納された 1 つの整数のコンテナを実装しnます。このコンテナは、 から にその値を渡すために使用されProducerますConsumer。コンテナが一度に保持できる値は 1 つだけなので、 と の両方ProducerConsumer、コンテナがいっぱいかどうかを何らかの方法で認識している必要があります。ブール値valueSetがこの指標です。

に設定されている場合true、コンテナは満杯であるため、Producer空になるまで待ってから再度充填する必要があります。同様に、valueSetが false の場合、 は取得するものがあるまでインスタンスConsumerのコンテンツを取得しようとしない場合があります。Q

ブール値 (およびその状態のテスト) を削除することにより、スレッドProducerConsumerスレッドの両方を通知の待機状態にします (コードでのみ生成できたため、通知はおそらく発生しません)。したがって、表示される唯一のメッセージは次のとおりです。メインスレッドからのもの。

非常に重要な点: Freedom_Benが彼自身の回答で示唆しているように、このコードはgetputメソッドの両方が行われるsynchronisedため機能します。つまりsynchronized、実行中に呼び出しを介してオブジェクトにアクセスしようとする他のすべてのスレッドをブロックし、これらの呼び出しをアトミックにします。お互い。この点は重要です。これにより、読み取りと書き込みの両方がアトミックに行われることがほぼ保証されるからvalueSetですn両方のメソッドでそのプロパティが設定されていない場合、 からの通知は、がチェックされた後 、 が呼び出される前にput発生する可能性があります。通知メカニズム (*) の実装によっては、これが原因である可能性があります。ConsumervalueSetwaitConsumerに値があるにもかかわらず、通知を見逃して待機状態になりますQ。これらのメソッドの属性により、synchronizedこれらの呼び出しが意図したとおりに動作することが保証されます。

  • synchronizedキーワード

  • notifywaitメソッド


(*)waitおよびnotifiyコードは、次の 2 つの方法で実装できます。

  • 手っ取り早い方法はnotify、スレッドが ing であるかどうかを単純にチェックwaitし、そうである場合はウェイクアップするか、それ以外は何もしないことです。synchronizedこれは、適切なメソッド呼び出しなしで競合状態につながるシナリオです。

  • より正しい方法は、 で初期化された専用のセマフォを使用し、 セマフォの(aka ) および(aka ) 操作にそれぞれおよびを0エイリアスすることです。notifiywaitincrementreleasedecrementacquire

于 2013-02-27T00:18:28.920 に答える