ホームページ >Java >&#&チュートリアル >Javaスレッド間通信待機/通知

Javaスレッド間通信待機/通知

巴扎黑
巴扎黑オリジナル
2017-06-26 11:38:571501ブラウズ

Java の Wait/notify/notifyAll は、スレッド間通信の実装に使用でき、Object クラスのメソッドです。これら 3 つのメソッドはすべてネイティブ メソッドであり、プロデューサー/コンシューマー モデルの実装によく使用されます。 。まず関連する定義を見てみましょう:

wait(): このメソッドを呼び出すスレッドは WATTING 状態に入り、wait() メソッドを呼び出した後、別のスレッドからの通知または割り込みを待機する場合にのみ戻ります。 、オブジェクトのロックが解除されます。

wait(long): タイムアウトは最大longミリ秒待機します。通知がない場合はタイムアウトして戻ります。

notify(): オブジェクトを待っているスレッドに wait() メソッドからの戻りを通知します。戻りの前提は、スレッドがオブジェクトのロックを取得することです。

notifyAll(): このオブジェクトを待機しているすべてのスレッドに通知します。

簡単な例をシミュレーションしてみましょう。階下に小さな餃子レストランがあります。シェフが毎回料理をするのを防ぐために、店にはシェフとウェイターがいます。ウェイターは 1 人前を持ってきましたが、あまりにも非効率でエネルギーの無駄でした。ここで、シェフが 10 人前を準備するたびに、ウェイターが大きな木の皿にそれを盛り付けて顧客に提供するとします。毎日 100 人前を販売した後、レストランは閉店し、シェフとウェイターは家に帰って休憩します。

考えてみてください、この機能を実装するために待機/通知メカニズムを使用しない場合、最も直接的な方法は、ウェイターが時々キッチンに行って皿の上の料理を取り出すことかもしれません10食後。この方法には 2 つの大きな欠点があります:

1. ウェイターがキッチンに行きすぎて疲れすぎている場合は、ボウルが作られるたびにゲストに提供する方が良いです。大きな木の板は映りません。実装コード レベルでの具体的な兆候は、継続的なループが必要となり、プロセッサ リソースが無駄になるということです。

2. ウェイターが久しぶりにキッチンに確認に行った場合、時間通りであることは保証できません。おそらくシェフはすでに 10 人前を調理しましたが、ウェイターはそれを守っていませんでした。 上記の例では、シェフが 10 人前を作るたびに、「

餃子の準備ができましたので、お持ち帰りいただけます」と叫びます。待機/通知メカニズムを使用する方がはるかに合理的です。ウェイターは通知を受け取ると、キッチンに行き、餃子をゲストに提供します。シェフはまだ十分に仕事をしていないため、シェフからの通知を受け取っていないため、少し休憩することができます しかし、耳を澄ましてシェフからの通知を待たなければなりません

 1 package ConcurrentTest; 2  3 import thread.BlockQueue; 4  5 /** 6  * Created by chengxiao on 2017/6/17. 7  */ 8 public class JiaoziDemo { 9     //创建个共享对象做监视器用10     private static Object obj = new Object();11     //大木盘子,一盘最多可盛10份饺子,厨师做满10份,服务员就可以端出去了。12     private static Integer platter = 0;13     //卖出的饺子总量,卖够100份就打烊收工14     private static Integer count = 0;15 16     /**17      * 厨师18      */19     static class Cook implements Runnable{20         @Override21         public void run() {22             while(count<100){23                 synchronized (obj){24                     while (platter<10){25                         platter++;26                     }27                     //通知服务员饺子好了,可以端走了28                     obj.notify();29                     System.out.println(Thread.currentThread().getName()+"--饺子好啦,厨师休息会儿");30                 }31                 try {32                     //线程睡一会,帮助服务员线程抢到对象锁33                     Thread.sleep(100);34                 } catch (InterruptedException e) {35                     e.printStackTrace();36                 }37             }38             System.out.println(Thread.currentThread().getName()+"--打烊收工,厨师回家");39         }40     }41 42     /**43      * 服务员44      */45     static class Waiter implements Runnable{46         @Override47         public void run() {48             while(count<100){49                 synchronized (obj){50                     //厨师做够10份了,就可以端出去了51                     while(platter < 10){52                         try {53                             System.out.println(Thread.currentThread().getName()+"--饺子还没好,等待厨师通知...");54                             obj.wait();55                             BlockQueue56                         } catch (InterruptedException e) {57                             e.printStackTrace();58                         }59                     }60                     //饺子端给客人了,盘子清空61                     platter-=10;62                     //又卖出去10份。63                     count+=10;64                     System.out.println(Thread.currentThread().getName()+"--服务员把饺子端给客人了");65                 }66             }67             System.out.println(Thread.currentThread().getName()+"--打烊收工,服务员回家");68 69         }70     }71     public static void main(String []args){72         Thread cookThread = new Thread(new Cook(),"cookThread");73         Thread waiterThread = new Thread(new Waiter(),"waiterThread");74         cookThread.start();75         waiterThread.start();76     }77 }
小さな例
ランニング結果

cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--打烊收工,服务员回家
cookThread--打烊收工,厨师回家
ランニング結果

動作メカニズム

wait/notify の動作メカニズムを理解するには、「The Art of Concurrent Programming」から画像を借用してください

いわゆるモニターとオブジェクトロックについてあまり知らない人もいるかもしれませんが、ここでは簡単な説明を示します説明:

jvm は、各オブジェクトおよびクラスにロックを関連付けます。オブジェクトをロックするとは、そのオブジェクトに関連付けられたモニターを取得することを意味します。

オブジェクトのロックが取得された場合にのみ、モニターを取得できます。ロックの取得に失敗した場合、スレッドはブロックキューに入ります。オブジェクトのロックの取得に成功した場合は、wait() メソッドを使用することもできます。モニター上で待機すると、ロックが解放され、待機キューに入ります。

ロックとモニターの違いについては、庭の友人が非常に詳細で徹底的な記事を書いていますので、興味のある子供たちはロックとモニターの違いについて学ぶことができます上図を基に、具体的な処理を整理してみます

1.まず、waitThreadがオブジェクトのロックを取得し、wait()メソッドを呼び出します この時、待機スレッドは

放棄します。 object lock、同時にオブジェクトの 待機キュー WaitQueue に入ります

2. NoticeThread スレッドはオブジェクト ロックを捕捉し、いくつかの操作を実行し、notify() メソッドを呼び出します。待機スレッド waitThread が待機キュー WaitQueue から同期に移動されます。キュー SynchronizedQueue で、waitThread が待機状態からブロック状態に変わります。

この時点では、notifyThread はすぐにロックを解放しないことに注意してください 実行は継続され、残りの作業が完了した後にのみロックが解放されます

3.waitThread は再度オブジェクト ロックを取得します。 wait() メソッドからオブジェクトのロックを取得し、後続の操作を続行します。

4. 待機/通知メカニズムに基づくスレッド間通信のプロセスが終了します。

notifyAll に関しては、2 番目のステップで、

待機キュー

内のすべてのスレッドが同期キューに移動されます。 落とし穴を避ける

wait/notify/notifyAll を使用する際に注意すべき特別な点がいくつかあります。

1 必ず wait()/notify()/notifyAll() を使用してください。つまり、ロックがロックされた後にのみモニターを取得できるため、これについては前に説明しました。それ以外の場合、jvm は IllegalMonitorStateException もスローします。

2. wait() を使用する場合、スレッドが待機状態に入るかを決定する条件は if ではなく while を使用する必要があります。待機中のスレッドが誤って目覚める可能性があるため、while ループを使用して最後に、安全を確保するためにウェイクアップ条件が満たされているかどうかを確認してください。

3. Notice() または NotifyAll() メソッドが呼び出された後、スレッドはすぐにはロックを解放しません。この呼び出しは、待機中のスレッドを待機キューから同期キューに移動するだけです。つまり、スレッドのステータスが待機状態からブロック状態に変わります。 4. wait() メソッドから戻る前提は、スレッドが呼び出し元のオブジェクトを取り戻すことです。ロック。 追記

今回はwait/notify関連の内容の紹介ですが、実際に使用する際には上記の点に特に注意してください。ただし、通常はwait/notify/notifyAllを直接使用してスレッド間を完結させます。 Java 同時実行パッケージには、さまざまな BlockingQueue などの優れた優れたツールがすでに提供されているため、プロデューサー/コンシューマー モデルの機会はそれほど多くありません。これについては、後で機会があれば詳しく紹介します。 励まし合いましょう

以上がJavaスレッド間通信待機/通知の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。