http://javarevisited.blogspot.com/2015/07/how-to-use-wait-notify-and-notifyall-in.html

If some thread is waiting for some condition to become true, you can use notify and notifyAll methods to inform them that condition is now changed and they can wake up.

Both notify() and notifyAll() method sends a notification but notify sends the notification to only one of the waiting thread, no guarantee which thread will receive notification and notifyAll() sends the notification to all threads.

How to use wait and notify in code

I am sure many will be stuck and make mistakes e.g. synchronizing at the wrong place, not calling wait on a right object or not following standard idiom. To be honest its confusing for non-regular coders.

First confusion arise from the fact, how to call wait() method? Since wait method is not defined in Thread class , you cannot simply call Thread.wait() , that won't work but since many Java developers are used to calling Thread.sleep() they try the same thing with wait() method and stuck.

You need to call wait() method on the object which is shared between two threads, in producer-consumer problem its the queue which is shared between producer and consumer threads.

The second confusion comes from the fact that wait needs to be a call from synchronized block or method? So if you use

synchronized block which object should be put to go inside the block? This should be the same object, whose lock you want to acquire i.e. the shared object between multiple threads. In our case it's queue.

Always call wait and notify from Loop Instead of If Block

Once you know that you need to call wait from synchronized context and on the shared object, next thing is to avoid mistake made by several Java developer by calling wait() method inside if block instead of while loop.

Since you call wait inside a conditional block e.g. producer thread should call wait() if queue is full, first instinct goes towards using if block, but calling wait() inside if block can lead to subtle bugs because it's possible for thread to wake up spuriously even when waiting condition is not changed.

If you don't check the condition again after waking up by using a loop, you will take the wrong action which may cause problem e.g. trying to insert item on a full queue or trying to consume from an empty queue. That's why you should always call wait and notify method from a loop and not from if block.

I also suggest reading Effective Java item on the same topic , probably the best reference in how to properly call wait and notify method.

Based upon above knowledge here is the standard code template or idiom to call wait and notify method in Java :

// The standard idiom for calling the wait method in Java 
synchronized (sharedObject) { 
    while (condition) { 
    sharedObject.wait(); 
        // (Releases lock, and reacquires on wakeup) 
    } 
    // do action based upon condition e.g. take or put into queue 
}

As I suggested, you should always invoke wait method from a loop. The loop is used to test the condition before and after waiting. If the condition still holds and the notify (or notifyAll) method has already been invoked before a thread calls wait() method, then there is no guarantee that the thread will ever awake from the wait, potentially causing a deadlock.

Java wait(), notify() and notifyAll() Example

import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
/**
 * Simple Java program to demonstrate How to use wait, notify and notifyAll()
 * method in Java by solving producer consumer problem.
 *
 * @author Javin Paul
 */
public class Main {
    public static void main(String args[]) {
        System.out.println("How to use wait and notify method in Java");
        System.out.println("Solving Producer Consumper Problem");
        Queue<Integer> buffer = new LinkedList<>();
        int maxSize = 10;
        Thread producer = new Producer(buffer, maxSize, "PRODUCER");
        Thread consumer = new Consumer(buffer, maxSize, "CONSUMER");
        producer.start();
        consumer.start();
    }
}
/**
 * Producer Thread will keep producing values for Consumer
 * to consumer. It will use wait() method when Queue is full
 * and use notify() method to send notification to Consumer
 * Thread.
 * @author WINDOWS 8
 * */
class Producer extends Thread {
    private Queue<Integer> queue;
    private int maxSize;
    public Producer(Queue<Integer> queue, int maxSize, String name) {
        super(name);
        this.queue = queue;
        this.maxSize = maxSize;
    }
    @Override public void run() {
        while (true) {
            synchronized (queue) {
                while (queue.size() == maxSize) {
                    try {
                        System.out .println("Queue is full, " + "Producer thread waiting for " + "consumer to take something from queue");
                        queue.wait();
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }
                Random random = new Random();
                int i = random.nextInt();
                System.out.println("Producing value : " + i);
                queue.add(i);
                queue.notifyAll();
            }
        }
    }
}
/**
 * Consumer Thread will consumer values form shared queue.
 * It will also use wait() method to wait if queue is
 * empty. It will also use notify method to send * notification to producer thread after consuming values
 * from queue.
 * @author WINDOWS 8
 **/
class Consumer extends Thread {
    private Queue<Integer> queue;
    private int maxSize;
    public Consumer(Queue<Integer> queue, int maxSize, String name){
        super(name);
        this.queue = queue;
        this.maxSize = maxSize;
    }
    @Override public void run() {
        while (true) {
            synchronized (queue) {
                while (queue.isEmpty()) {
                    System.out.println("Queue is empty," + "Consumer thread is waiting" + " for producer thread to put something in queue");
                    try {
                        queue.wait();
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                } System.out.println("Consuming value : " + queue.remove());
                queue.notifyAll();
            }
        }
    }
}

本文重点:

  1. 你可以使用wait和notify函数来实现线程间通信。你可以用它们来实现多线程(>3)之间的通信。

  2. 永远在synchronized的函数或对象里使用wait、notify和notifyAll,不然Java虚拟机会生成 IllegalMonitorStateException。

  3. 永远在while循环里而不是if语句下使用wait。这样,循环会在线程睡眠前后都检查wait的条件,并在条件实际上并未改变的情况下处理唤醒通知。

  4. 永远在多线程间共享的对象(在生产者消费者模型里即缓冲区队列)上使用wait。

  5. 基于前文提及的理由,更倾向用 notifyAll(),而不是 notify()。

二者的应用场景在并发情况下还是有所不同的。

当一个线程进入wait之后,就必须等其他线程notify/notifyall,使用notifyall,可以唤醒

所有处于wait状态的线程,使其重新进入锁的争夺队列中,而notify只能唤醒一个。注意,任何时候只有一个线程可以获得锁,也就是说只有一个线程可以运行synchronized 中的代码,notifyall只是让处于wait的线程重新拥有锁的争夺权,但是只会有一个获得锁并执行。

那么notify和notifyall在效果上又什么实质区别呢?

https://stackoverflow.com/questions/37026/java-notify-vs-notifyall-all-over-again

Clearly,notifywakes (any) one thread in the wait set,notifyAllwakes all threads in the waiting set. The following discussion should clear up any doubts.notifyAllshould be used most of the time. If you are not sure which to use, then usenotifyAll.Please see explanation that follows.

死锁

Usenotify()if all your waiting threads are interchangeable (the order they wake up doesn't matter), or if you only ever have one waiting thread. A common example is a thread pool used to execute jobs from a queue--when a job is added, one of threads is notified to wake up, execute the next job and go back to sleep.

  • UsenotifyAll()for other cases where the waiting threads may have different purposes and should be able to run concurrently. An example is a maintenance operation on a shared resource, where multiple threads are waiting for the operation to complete before accessing the resource.

results matching ""

    No results matching ""