1.3スレッド間のJAVA並行システムのコラボレーション

場合は、タスクのコラボレーション、重要な問題は、これらのタスクの間でハンドシェイク/コミュニケーション

相互に排他的:このハンドシェイク/通信を実現するために、我々は同じ基本的な特性を使用しています。ミューテックスを使用すると、すべての可能な競合状態を根絶することができそうという、1つのタスクのみが信号に応答することができることを確実にします。ミューテックス以上、私たちは仕事のための方法を追加し、あなたには、いくつかの外部条件が変化するまで自体を一時停止することができます(たとえば、今の場所でパイプライン)は、それが先に開始日の仕事のための時間であることを示します。

このハンドシェイク/通信は、オブジェクトの手段によるものであってもよい)((WAIT)と通知安全に達成します。また、同時クラスライブラリ有して提供のawaitと信号()のメソッド条件オブジェクトを。私たちは、あらゆる種類の問題、および対応するソリューションが表示されます。

通知和待ちます

農産物変化に外の世界を待っている間にタスクがハングアップするのを待つだろう、と通知または発生のnotifyAllた場合にのみ、このタスクは起こされるだろうし、生じた変更を確認します。そうでないビジーウェイトを待ちます

コールスレッドを実行するときに待機するが別のタスクがロックを得ることができることを意味し、オブジェクトのロックが解除され、中断されます。

  • ないビジーウェイト待ち
  • コール待機はロックを解除します
    • ロックが解除されていないコール睡眠、収量

呼制御ブロックシンク待ちでのみ同期制御方法は、通知、およびのnotifyAllあなたがプログラムのコンパイル、非同期制御方法でこれらのメソッドを呼び出しますが、実行している場合、それは次のようになりますIllegalMonitorStateException例外、とのようないくつかのあいまいなメッセージを伴って「現在のスレッドが所有者ではありません。」(メッセージを意味することは、待機呼び出すこれらのメソッドを呼び出す前に通知してnotifyAll作業することでなければならない「自分」(GET)ロックオブジェクト)

待ち時間の2つの形式があります。

メソッドのパラメータと同じ意味をパラメータとして秒数を受け入れて、そして眠りにボーの最初のバージョンは意味を参照してください「この一時停止中。」しかし、待機と睡眠は、待機のために異なっている中で、スリープ状態に差異待ち、次のように:

  1. 待ちオブジェクトのロック中に放出されます。
  2. あなたは、のnotifyAllを通知し、または待機から実行を再開するために、時間が経過することができます。

スレッドが通知するか、のnotifyAllメッセージが受信されるまで待つの第二は、任意のパラメータ(また、より一般的に使用される形態)を受け入れていない、この待機は無期限に待機します。

待機、通知とのnotifyAll特別な側面は、これらのメソッドは、基本クラスのオブジェクトの一部ではなく、スレッドの一部であるということです。ロックは、これらのオブジェクトの操作方法のすべての部分であるので、これは、理にかなっています。

待機を呼び出す前に、通知とのnotifyAllタスクは、「自分の」ロック(GET)オブジェクトは、(以下の例を参照)しなければならないこれらのメソッドを呼び出します

それは他の目的は、独自のロックを維持するために、特定のアクションを実行することができます。そのためには、まずロックオブジェクトを取得する必要があります。あなたがオブジェクトのxのnotifyAllを送信したい場合は、我々は内同期制御ブロックXロックを達成することができますので、例えば、それは実行する必要があります。

synchronized{
    x.notifyAll();
}

プログラムの例

TODO:コード:こちらを取り除きます

class Car {
  private boolean waxOn = false;
  public synchronized void waxed() {
    waxOn = true; // Ready to buff
    notifyAll();
  }
  public synchronized void buffed() {
    waxOn = false; // Ready for another coat of wax
    notifyAll();
  }
  public synchronized void waitForWaxing()
  throws InterruptedException {
    while(waxOn == false)
      wait();
  }
  public synchronized void waitForBuffing()
  throws InterruptedException {
    while(waxOn == true)
      wait();
  }
}

class WaxOn implements Runnable {
  private Car car;
  public WaxOn(Car c) { car = c; }
  public void run() {
    try {
      while(!Thread.interrupted()) {
        printnb("Wax On! ");
        TimeUnit.MILLISECONDS.sleep(200);
        car.waxed();
        car.waitForBuffing();
      }
    } catch(InterruptedException e) {
      print("Exiting via interrupt");
    }
    print("Ending Wax On task");
  }
}

class WaxOff implements Runnable {
  private Car car;
  public WaxOff(Car c) { car = c; }
  public void run() {
    try {
      while(!Thread.interrupted()) {
        car.waitForWaxing();
        printnb("Wax Off! ");
        TimeUnit.MILLISECONDS.sleep(200);
        car.buffed();
      }
    } catch(InterruptedException e) {
      print("Exiting via interrupt");
    }
    print("Ending Wax Off task");
  }
}

public class WaxOMatic {
  public static void main(String[] args) throws Exception {
    Car car = new Car();
    ExecutorService exec = Executors.newCachedThreadPool();
    exec.execute(new WaxOff(car));
    exec.execute(new WaxOn(car));
    TimeUnit.SECONDS.sleep(5); // Run for a while...
    exec.shutdownNow(); // Interrupt all tasks
  }
} /* Output: (95% match)
Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Exiting via interrupt
Ending Wax On task
Exiting via interrupt
Ending Wax Off task
*///:~

それがfalseの場合はここでは、車は、単一のブール属性waxOnを持ってwaxOnマークをチェックしますwaitForWaxingで研磨ワックスの状態を示し、その後、呼び出しタスクが待機を呼び出すことによって中断されます。この動作は、同期メソッドで発生し、このようなプロセスでは、これは、重要であり、タスクがロックを獲得しましたあなたが待機を呼び出すとロックが解除されている間、スレッドは、中断されます。ロックが解除され、安全にオブジェクトの状態を変更するためにあるため、これは本質であるさ(例えばwaxOnは、真である保留中のタスクが継続する場合は、あなたがアクションを実行しなければならない変更)、いくつかの他のタスクのことができるようにしなければなりませんロックを取得しますこの場合は、別のタスクの呼び出しは「何かをする時間である」を意味するようにワックスをかけた場合、その後、あなたは、ロックを取得する必要がtrueにwaxOnが変更されます、**ワックス()呼び出しの後のnotifyAll、それが中断されているタスクで待機する呼び出しで目を覚ますだろう。それはロックを解除するために待機に入ったときに、待機からこのタスクのウェイクアップを行うためには、まず取り戻す必要があります。ロックが使用可能になる前に**、このタスクは°目覚めされることはありません。

戦略:あなたは、この非常に重要なを確認するために、関心の条件でループしながら、サラウンド待ち時間を強調しなければならない前の例ので、:

  • あなたはロックの同じ待機中に同じ理由で複数のタスクを有していてもよく、そして最初のウェイクアップタスクが(そうでない場合でも、誰かが継承によってこれを行うには、あなたのクラスになります)このような状況を変更することがあります。このような場合は、それ面白いの条件が変更されるまで、このタスクは再び中断されなければなりません。
  • このタスクは、その待機から起こされた時点では、そこにこのタスクは、この時点で実行することができないので、他のいくつかのタスクの変更が行われたこと、または操作が無関係となっている実行してもよいです。この時点で、再ハングする再び待機を呼び出すことでなければなりません。
  • これは、いくつかのタスクは、被写体のロックを待っている別の理由(この場合のnotifyAllを使用しなければならない)ことも可能です。ない場合は、このケースでは、あなたは右の理由によりウェイクアップするかどうかをチェックする必要があり、したがって、再び、待機呼び出します

その本質は、関心のある特定の条件をチェックすることで、条件が満たされていない場合には待機するように戻りました。従来の方法は、そのようなコードを記述するために使用されます

和のnotifyAllを通知

技術的なので、これだけ通知呼び出しよりも安全であるとのnotifyAllを呼び出して、単一Carオブジェクトの待機状態にある複数のタスクがあるかもしれません。しかし、上記の手順の構造は、(WaxOMaticあなたは通知の代わりのnotifyAll使用することができますので、コードは)のみ、待機状態での実際の仕事を持っています

使用のnotifyAllが最適化されるのではなく通知します。通知を使用する場合は、一つだけでは、通知を使用したい場合はそう、タスクを起こされることが適切であることを確認する必要があり、ロックを待っている多くのタスクで目覚めます。また、使用が通知するために、すべてのタスクが同じ条件を待たなければなりません(TODO:まだ彼らは???ここに来る)、複数のタスクが異なる条件を待っているしている場合ので、あなたは、適切なタスクをウェイクするかどうかを知ることができません。あなたは状況が変化したときに、通知を使用している場合、あなたはそれの恩恵を受けることができる唯一のタスクが存在しなければなりません。最後に、提示しなければならないかもしれすべてのサブクラスでこれらの制限は、常に役割を果たしています。これらのルールのいずれかが満たされていない場合は、使用のnotifyAll代わりに通知しなければなりません

「すべてのタスクを待っている。」目を覚ますだろうのnotifyAll プログラムでこの平均のどこでも、タスクをきっかけに、待機中の任意の状態は、それがあるのnotifyAllへの呼び出しになりますでしょうか?実際には、原因特定のロックへのnotifyAllが呼び出されたときに、唯一のタスクのロックを待つためには目覚めされます:あなたは、次のコード例を見ることができます。

あなたが目覚めすべきタスクを知らないので、より複雑なケースでは、特定のオブジェクトのロック待ちで複数のタスクがあるかもしれません。したがって、あなたがロックを待っているすべてのタスクを覚ますことができ、各タスクは、それらに関連する通知するかどうかを決定しなければならないので、より安全であるとのnotifyAllを呼び出して。

TODO:コード:こちらを取り除きます

class Blocker {
  synchronized void waitingCall() {
    try {
      while(!Thread.interrupted()) {
        wait();
        System.out.print(Thread.currentThread() + " ");
      }
    } catch(InterruptedException e) {
      // OK to exit this way
    }
  }
  synchronized void prod() { notify(); }
  synchronized void prodAll() { notifyAll(); }
}

class Task implements Runnable {
  static Blocker blocker = new Blocker();
  public void run() { blocker.waitingCall(); }
}

class Task2 implements Runnable {
  // A separate Blocker object:
  static Blocker blocker = new Blocker();
  public void run() { blocker.waitingCall(); }
}

public class NotifyVsNotifyAll {
  public static void main(String[] args) throws Exception {
    ExecutorService exec = Executors.newCachedThreadPool();
    for(int i = 0; i < 5; i++)
      exec.execute(new Task());
    exec.execute(new Task2());
    Timer timer = new Timer();
    timer.scheduleAtFixedRate(new TimerTask() {
      boolean prod = true;
      public void run() {
        if(prod) {
          System.out.print("\nnotify() ");
          Task.blocker.prod();
          prod = false;
        } else {
          System.out.print("\nnotifyAll() ");
          Task.blocker.prodAll();
          prod = true;
        }
      }
    }, 400, 400); // Run every .4 second
    TimeUnit.SECONDS.sleep(5); // Run for a while...
    timer.cancel();
    System.out.println("\nTimer canceled");
    TimeUnit.MILLISECONDS.sleep(500);
    System.out.print("Task2.blocker.prodAll() ");
    Task2.blocker.prodAll();
    TimeUnit.MILLISECONDS.sleep(500);
    System.out.println("\nShutting down");
    exec.shutdownNow(); // Interrupt all tasks
  }
} /* Output: (Sample)
notify() Thread[pool-1-thread-1,5,main]
notifyAll() Thread[pool-1-thread-1,5,main] Thread[pool-1-thread-5,5,main] Thread[pool-1-thread-4,5,main] Thread[pool-1-thread-3,5,main] Thread[pool-1-thread-2,5,main]
notify() Thread[pool-1-thread-1,5,main]
notifyAll() Thread[pool-1-thread-1,5,main] Thread[pool-1-thread-2,5,main] Thread[pool-1-thread-3,5,main] Thread[pool-1-thread-4,5,main] Thread[pool-1-thread-5,5,main]
notify() Thread[pool-1-thread-1,5,main]
notifyAll() Thread[pool-1-thread-1,5,main] Thread[pool-1-thread-5,5,main] Thread[pool-1-thread-4,5,main] Thread[pool-1-thread-3,5,main] Thread[pool-1-thread-2,5,main]
notify() Thread[pool-1-thread-1,5,main]
notifyAll() Thread[pool-1-thread-1,5,main] Thread[pool-1-thread-2,5,main] Thread[pool-1-thread-3,5,main] Thread[pool-1-thread-4,5,main] Thread[pool-1-thread-5,5,main]
notify() Thread[pool-1-thread-1,5,main]
notifyAll() Thread[pool-1-thread-1,5,main] Thread[pool-1-thread-5,5,main] Thread[pool-1-thread-4,5,main] Thread[pool-1-thread-3,5,main] Thread[pool-1-thread-2,5,main]
notify() Thread[pool-1-thread-1,5,main]
notifyAll() Thread[pool-1-thread-1,5,main] Thread[pool-1-thread-2,5,main] Thread[pool-1-thread-3,5,main] Thread[pool-1-thread-4,5,main] Thread[pool-1-thread-5,5,main]
Timer canceled
Task2.blocker.prodAll() Thread[pool-1-thread-6,5,main]
Shutting down
*///:~

あなたは、出力から見ることができますTask2のはTask2.blockerのが存在をブロック、また内の任意のオブジェクト場合でも、Task.blocker通知または上の呼び出しのnotifyAllタスクが起こされるためにオブジェクトつながります同様に、メインの通話の終了時に、タイマーをキャンセルし、タイマーが取り消された場合でも、最初の5つのタスクがまだ実行されている、と彼らに残ってTask.blocker.waitingCallコールがブロックされています。Task2.blocker.prodAll呼び出しによって生成された出力任意の含まれていないTask.blockerロックを待っているタスクを。

あなたがブロッカーを閲覧した場合prodprodAll、あなたはこの作るセンスがあります。これらのメソッドは、彼らがすることをその手段、同期化され、独自のロックを取得するので、唯一のこの特定のロックタスクを待っているに目を覚ますだろう-彼らは通知したりのnotifyAll、単に論理的なロックを呼び出すために呼び出すようにする場合、。

堅牢性を確保するために、以下のいくつかの段落ことに注意してください

Blocker.waitingCallそのため、この場合には、あなたは、単に宣言することは非常に簡単なfor(;;)代わりにwhile(!Thread interrupted())、この場合には、異常なサイクルのために残されているため、あなたは同じ効果を得ることができますし、中断したフラグをチェックすることにより、ループを残すに違いはありません- 2で小文字は、同じコードを実行しなければなりません。しかし、実際には、この例では、サイクルを残すには二つの方法があるので、中断しチェックすることにしました。後で、あなたがループ内でより多くのコードを追加することを決定した場合、これはこのサイクルから出口までの二つの経路をカバーしていない場合は、そう、それはエラーを導入する危険性があります。

同時アプリケーションでは、他のいくつかのタスクがでてWaitPerson時間起こされる、急に関与し、受注を奪うれます、唯一の安全な方法は、この待機の次のイディオムを使用することです(もちろん、適切な同期で、内部の使用を防止するために見逃した信号プログラム設計の可能性)

while(conditionIsNotMet)
	wait();

この性を保証あな​​たは、待ちループ条件が満たされる終了前に、あなたが何かについて通知を受けるが、それはこの状態をどうするか、完全に蚊帳の外に、条件が変更されているようになるまで待つことは何も持っていない場合、あなたが待機状態に復帰できることを確認することができます。

ロックと条件オブジェクト

タスクを目覚めさせるために、またはお電話するように、信号を呼び出してタスクを通知するようにタスクを中断する条件のawaitに呼び出すことによってsignalAllのnotifyAllに比べてタスクを、保留この状態で全て自身で目を覚ますために、signalAllそれはより多くのです安全な方法

同期キュー

同期キュー:BlockingQueue、、 LinkedBlockingQueueArrayBlockingQueue取得しようと、消費者のタスクはキューからオブジェクトが、今回は、キューが空の場合、キューはまた、消費者のタスクを一時停止し、消費者のタスクを復元するために利用できるより多くの要素があることができます。使用BlockingQueue簡略生成、明示的なウェイトの存在との間の結合と、クラス及び被削性追加を通知する場合ので、各クラスのみ彼のBlockingQueue通信、即ち、この注例以下のプロデューサとコンシューマ:

TODO:コード:こちらを取り除きます

class Toast {
  public enum Status { DRY, BUTTERED, JAMMED }
  private Status status = Status.DRY;
  private final int id;
  public Toast(int idn) { id = idn; }
  public void butter() { status = Status.BUTTERED; }
  public void jam() { status = Status.JAMMED; }
  public Status getStatus() { return status; }
  public int getId() { return id; }
  public String toString() {
    return "Toast " + id + ": " + status;
  }
}

class ToastQueue extends LinkedBlockingQueue<Toast> {}

class Toaster implements Runnable {
  private ToastQueue toastQueue;
  private int count = 0;
  private Random rand = new Random(47);
  public Toaster(ToastQueue tq) { toastQueue = tq; }
  public void run() {
    try {
      while(!Thread.interrupted()) {
        TimeUnit.MILLISECONDS.sleep(
          100 + rand.nextInt(500));
        // Make toast
        Toast t = new Toast(count++);
        print(t);
        // Insert into queue
        toastQueue.put(t);
      }
    } catch(InterruptedException e) {
      print("Toaster interrupted");
    }
    print("Toaster off");
  }
}

// Apply butter to toast:
class Butterer implements Runnable {
  private ToastQueue dryQueue, butteredQueue;
  public Butterer(ToastQueue dry, ToastQueue buttered) {
    dryQueue = dry;
    butteredQueue = buttered;
  }
  public void run() {
    try {
      while(!Thread.interrupted()) {
        // Blocks until next piece of toast is available:
        Toast t = dryQueue.take();
        t.butter();
        print(t);
        butteredQueue.put(t);
      }
    } catch(InterruptedException e) {
      print("Butterer interrupted");
    }
    print("Butterer off");
  }
}

// Apply jam to buttered toast:
class Jammer implements Runnable {
  private ToastQueue butteredQueue, finishedQueue;
  public Jammer(ToastQueue buttered, ToastQueue finished) {
    butteredQueue = buttered;
    finishedQueue = finished;
  }
  public void run() {
    try {
      while(!Thread.interrupted()) {
        // Blocks until next piece of toast is available:
        Toast t = butteredQueue.take();
        t.jam();
        print(t);
        finishedQueue.put(t);
      }
    } catch(InterruptedException e) {
      print("Jammer interrupted");
    }
    print("Jammer off");
  }
}

// Consume the toast:
class Eater implements Runnable {
  private ToastQueue finishedQueue;
  private int counter = 0;
  public Eater(ToastQueue finished) {
    finishedQueue = finished;
  }
  public void run() {
    try {
      while(!Thread.interrupted()) {
        // Blocks until next piece of toast is available:
        Toast t = finishedQueue.take();
        // Verify that the toast is coming in order,
        // and that all pieces are getting jammed:
        if(t.getId() != counter++ ||
           t.getStatus() != Toast.Status.JAMMED) {
          print(">>>> Error: " + t);
          System.exit(1);
        } else
          print("Chomp! " + t);
      }
    } catch(InterruptedException e) {
      print("Eater interrupted");
    }
    print("Eater off");
  }
}

public class ToastOMatic {
  public static void main(String[] args) throws Exception {
    ToastQueue dryQueue = new ToastQueue(),
               butteredQueue = new ToastQueue(),
               finishedQueue = new ToastQueue();
    ExecutorService exec = Executors.newCachedThreadPool();
    exec.execute(new Toaster(dryQueue));
    exec.execute(new Butterer(dryQueue, butteredQueue));
    exec.execute(new Jammer(butteredQueue, finishedQueue));
    exec.execute(new Eater(finishedQueue));
    TimeUnit.SECONDS.sleep(5);
    exec.shutdownNow();
  }
} /* (Execute to see output) *///:~

不在信号(潜在的なデッドロック)

T1:
synchronized(sharedMonitor){
    <setup condition for T2>
    sharedMonitor.notify();
}
T2:
while(someCondition){
    //point 1
    synchronized(sharedMonitor){
        sharedMonitor.wait();
    }
}

逃した信号(デッドロック):T2用と仮定がsomeCondition評価され、真であることが判明が、点1におけるスレッドスケジューラは、T1に切り替えられる(すなわち、<T2のセットアップ条件>)を設定T1を行い、次に通知泣き声、T2は次に進みます。この時間T2の場合は、機会が変更されている。この状態を認識することができないので、盲目的に待機に、この時間は逃し通知し、この信号が送信されているためT2が無期限に待機しているだろうことを、遅すぎました、デッドロックが生じ。

ソリューション:(防ぐために、someCondition変数の任意のレース)

synchronized(sharedMonitor){
    while(someCondition){
        sharedMonitor.wait();            
    }
}

今:制御が返されたときもしT1が最初に実行され、T2、それは待機を入力しないように、条件の変更があります。T2が最初に行われた場合には逆に、それは待ちに入り、後でT1によって目を覚まします。したがって、信号は見逃すことはありません。

おすすめ

転載: www.cnblogs.com/cheaptalk/p/12549677.html