ホームページ  >  記事  >  Java  >  Java 同時実行でスレッド間で連携する 2 つの方法: wait、notify、notifyAll、Condition

Java 同時実行でスレッド間で連携する 2 つの方法: wait、notify、notifyAll、Condition

黄舟
黄舟オリジナル
2017-03-18 10:09:081224ブラウズ

スレッド間のコラボレーション。たとえば、最も古典的な生産者と消費者のモデルです。キューがいっぱいの場合、生産者はキューに商品を投入し続ける前に、キューにスペースができるまで待つ必要があります。待機期間中、生産者は重要なリソースを解放する必要があります。つまり、キュー)の占有権。なぜなら、生産者が重要なリソースを占有する権利を解放しない場合、消費者はキュー内の商品を消費できず、キューにスペースがなくなり、生産者は無期限に待機することになるからです。したがって、通常の状況では、キューがいっぱいになると、プロデューサーは重要なリソースを占有する権利を引き渡して一時停止状態に入るように求められます。次に、消費者が商品を消費するのを待ち、消費者はキューに空きがあることを生産者に通知します。同様に、キューが空の場合、コンシューマは、キューに項目があることをプロデューサから通知されるまで待つ必要があります。この相互通信のプロセスがスレッド間の協力です。

wait()、notify()、およびnotifyAll()

[code]/**
 * Wakes up a single thread that is waiting on this object's
 * monitor. If any threads are waiting on this object, one of them
 * is chosen to be awakened. The choice is arbitrary and occurs at
 * the discretion of the implementation. A thread waits on an object's
 * monitor by calling one of the wait methods
 */
public final native void notify();

/**
 * Wakes up all threads that are waiting on this object's monitor. A
 * thread waits on an object's monitor by calling one of the
 * wait methods.
 */
public final native void notifyAll();

/**
 * Causes the current thread to wait until either another thread invokes the
 * {@link java.lang.Object#notify()} method or the
 * {@link java.lang.Object#notifyAll()} method for this object, or a
 * specified amount of time has elapsed.
 * <p>
 * The current thread must own this object&#39;s monitor.
 */
public final native void wait(long timeout) throws InterruptedException;

1) wait()、notify()、およびnotifyAll()メソッドはローカルメソッドであり最終メソッドであり、オーバーライドできません。
2) オブジェクトの wait() メソッドを呼び出すと、現在のスレッドがブロックされる可能性があり、現在のスレッドはこのオブジェクトのモニター (つまり、ロック) を所有する必要があります
3) オブジェクトの Notice() メソッドを呼び出すと、オブジェクトをウェイクアップできるこのオブジェクトを待機しているスレッドの監視。このオブジェクトの監視を待機しているスレッドが複数ある場合、そのうちの 1 つだけを起動できます。
4) NoticeAll() メソッドを呼び出すと、このオブジェクトの監視を待機しているすべてのスレッドを起動できます。 object;
友人の中には次のような疑問があるかもしれません: これら 3 つのメソッドは Thread クラスで宣言されず、Object クラスで宣言されているのはなぜですか (もちろん、Thread クラスは Object クラスを継承しているため、Thread は 3 つのメソッドを呼び出すこともできます)。 )?実際、この問題は非常に単純です。各オブジェクトにはモニター (つまり、ロック) があるため、現在のスレッドがオブジェクトのロックを待っている場合は、当然、このオブジェクトを通じて操作する必要があります。現在のスレッドを使用して操作する代わりに、現在のスレッドが複数のスレッドのロックを待機している可能性があるため、スレッドを介して操作するのは非常に複雑になります。
上で述べたように、オブジェクトの wait() メソッドが呼び出される場合、現在のスレッドはこのオブジェクトのモニター (つまりロック) を所有している必要があるため、wait() メソッドは同期ブロックまたは同期メソッド (同期ブロックまたは同期メソッド)。
オブジェクトの wait() メソッドを呼び出すことは、現在のスレッドにこのオブジェクトのモニターを引き渡して待機状態に入り、その後にこのオブジェクトのロックが再度取得されるのを待つことと同じです (sleep メソッドThread クラスは、現在のスレッドに一定期間実行を一時停止させ、他のスレッドに実行を継続する機会を与えますが、オブジェクトのロックは解放しません)。オブジェクトのモニターを複数のスレッドが待機している場合、そのうちの 1 つのスレッドのみが起動され、どのスレッドが起動されるかは不明です。
同様に、オブジェクトのnotify()メソッドを呼び出す場合、現在のスレッドはこのオブジェクトのモニターも所有している必要があるため、notify()メソッドの呼び出しは同期ブロックまたは同期メソッド(同期ブロックまたは同期メソッド)で行う必要があります。 。
nofityAll()メソッドは、notify()メソッドとは異なり、オブジェクトの監視を待機しているすべてのスレッドをウェイクアップできます。
ここで注意すべき点が 1 つあります。notify() メソッドと NotifyAll() メソッドは、オブジェクトのモニターを待機しているスレッドを起動するだけであり、どのスレッドがモニターを取得できるかは決定しません。
簡単な例を挙げます。 Thread1、Thread2、Thread3 という 3 つのスレッドがあり、すべてオブジェクト objectA のモニターを待機しているとします。この時点で、Thread4 がオブジェクト objectA のモニターを所有しています。その後、Thread4 で objectA.notify() メソッドが呼び出されます。 Thread1、Thread2、Thread3 は 1 つだけ起動できます。目覚めたからといって、objectA のモニターがすぐに取得されるわけではないことに注意してください。 objectA.notifyAll() メソッドが Thread4 で呼び出された場合、Thread1、Thread2、および Thread3 の 3 つのスレッドが起動され、次にどのスレッドが objectA のモニターを取得できるかは、オペレーティング システムのスケジューリングによって決まります。
上記の点に特に注意してください。スレッドが起動されても、notify() または NoticeAll() を呼び出して同期ブロックを終了し、オブジェクトのロックを解放した後でのみ、オブジェクトのモニターを取得できるわけではありません。スレッドは実行用のロックを取得します。

[code]public class Test {
    public static Object object = new Object();
    public static void main(String[] args) {
        Thread1 thread1 = new Thread1();
        Thread2 thread2 = new Thread2();

        thread1.start();

        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        thread2.start();
    }

    static class Thread1 extends Thread{
        @Override
        public void run() {
            synchronized (object) {
                try {
                    object.wait();
                } catch (InterruptedException e) {
                }
                System.out.println("线程"+Thread.currentThread().getName()+"获取到了锁");
            }
        }
    }

    static class Thread2 extends Thread{
        @Override
        public void run() {
            synchronized (object) {
                object.notify();
                System.out.println("线程"+Thread.currentThread().getName()+"调用了object.notify()");
            }
            System.out.println("线程"+Thread.currentThread().getName()+"释放了锁");
        }
    }
}

Condition

Conditionは、従来のオブジェクトのwait()とnotify()を置き換えて、スレッド間の連携を実現するために使用されます。スレッド間のコラボレーションを実現するには、Condition1 の await() と signal() を使用する方が安全かつ効率的です。したがって、一般的には、Condition を使用することをお勧めします。

Condition はインターフェイスであり、基本的なメソッドは await() メソッドと signal() メソッドです。
Condition は Lock インターフェイスに依存し、Condition を生成する基本コードは lock.newCondition( )
Call Condition の await () メソッドと signal() メソッドはロック保護内にある必要があります。つまり、lock.lock() と lock.unlock の間で使用する必要があります

Conditon中的await()对应Object的wait();
  Condition中的signal()对应Object的notify();
  Condition中的signalAll()对应Object的notifyAll()。
[code]public class Test {
    private int queueSize = 10;
    private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize);

    public static void main(String[] args)  {
        Test test = new Test();
        Producer producer = test.new Producer();
        Consumer consumer = test.new Consumer();

        producer.start();
        consumer.start();
    }

    class Consumer extends Thread{

        @Override
        public void run() {
            consume();
        }

        private void consume() {
            while(true){
                synchronized (queue) {
                    while(queue.size() == 0){
                        try {
                            System.out.println("队列空,等待数据");
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            queue.notify();
                        }
                    }
                    queue.poll();          //每次移走队首元素
                    queue.notify();
                    System.out.println("从队列取走一个元素,队列剩余"+queue.size()+"个元素");
                }
            }
        }
    }

    class Producer extends Thread{

        @Override
        public void run() {
            produce();
        }

        private void produce() {
            while(true){
                synchronized (queue) {
                    while(queue.size() == queueSize){
                        try {
                            System.out.println("队列满,等待有空余空间");
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            queue.notify();
                        }
                    }
                    queue.offer(1);        //每次插入一个元素
                    queue.notify();
                    System.out.println("向队列取中插入一个元素,队列剩余空间:"+(queueSize-queue.size()));
                }
            }
        }
    }
}
[code]public class Test {
    private int queueSize = 10;
    private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize);
    private Lock lock = new ReentrantLock();
    private Condition notFull = lock.newCondition();
    private Condition notEmpty = lock.newCondition();

    public static void main(String[] args)  {
        Test test = new Test();
        Producer producer = test.new Producer();
        Consumer consumer = test.new Consumer();

        producer.start();
        consumer.start();
    }

    class Consumer extends Thread{

        @Override
        public void run() {
            consume();
        }

        private void consume() {
            while(true){
                lock.lock();
                try {
                    while(queue.size() == 0){
                        try {
                            System.out.println("队列空,等待数据");
                            notEmpty.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    queue.poll();                //每次移走队首元素
                    notFull.signal();
                    System.out.println("从队列取走一个元素,队列剩余"+queue.size()+"个元素");
                } finally{
                    lock.unlock();
                }
            }
        }
    }

    class Producer extends Thread{

        @Override
        public void run() {
            produce();
        }

        private void produce() {
            while(true){
                lock.lock();
                try {
                    while(queue.size() == queueSize){
                        try {
                            System.out.println("队列满,等待有空余空间");
                            notFull.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    queue.offer(1);        //每次插入一个元素
                    notEmpty.signal();
                    System.out.println("向队列取中插入一个元素,队列剩余空间:"+(queueSize-queue.size()));
                } finally{
                    lock.unlock();
                }
            }
        }
    }
}

以上就是java-并发-线程间协作的两种方式:wait、notify、notifyAll和Condition的内容,更多相关内容请关注PHP中文网(www.php.cn)!

相关文章:

java notify和notifyAll的对比详细介绍

wait, notify 和 notifyAll的正确用法

通过实例讨论notify()和notifyAll()的本质区别

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