Olaseni
Olaseni

Reputation: 7916

A simple scenario using wait() and notify() in java

Can I get a complete simple scenario i.e. tutorial that suggest how this should be used, specifically with a Queue?

Upvotes: 213

Views: 266800

Answers (8)

Shashank Bodkhe
Shashank Bodkhe

Reputation: 1032

For a simple scenario of wait notify we will consider 2 threads :

1)Thread "Calculate" which gives us sum of all number from 0 to 5

2)Thread "WaitNotifyDemo" which is main thread

Objective : We want WaitNotifyDemo thread to wait till Calculate thread completes the calculation

Step 1] First lets create a thread that calculates the sum:

class Calculate extends Thread {
int sum;

@Override
public void run() {
    synchronized (this) {
        for (int i = 0; i < 5; i++) {
            sum += i;
            System.out.println("Current value of sum : " + sum);
        }
        notify();
    }
}

}

This class has :

  • Thread Class extended
  • We are synchronizing(this) as Main thread must have lock on "Calculate" thread
  • Calculating sum of numbers till 5

Step 2] lets create class which uses wait method which is used to make the Main thread till calculation completes

public class WaitNotifyDemo {

public static void main(String[] args) {
    Calculate t = new Calculate();
    t.start();
    synchronized (t) {
        try {
            t.wait();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("final total : " + t.sum);
    }
}

This class has :

  • Instance of Calculate Thread
  • Then we call wait() method on that instance

Output

enter image description here

So we can see that we have called wait on an object which makes main thread wait for calculation and then executes remaining code of the class.

Upvotes: 1

polygenelubricants
polygenelubricants

Reputation: 383746

Even though you asked for wait() and notify() specifically, I feel that this quote is still important enough:

Josh Bloch, Effective Java 2nd Edition, Item 69: Prefer concurrency utilities to wait and notify (emphasis his):

Given the difficulty of using wait and notify correctly, you should use the higher-level concurrency utilities instead [...] using wait and notify directly is like programming in "concurrency assembly language", as compared to the higher-level language provided by java.util.concurrent. There is seldom, if ever, reason to use wait and notify in new code.

Upvotes: 46

Remis Haroon - رامز
Remis Haroon - رامز

Reputation: 3572

The question asks for a wait() + notify() involving a queue(buffer). The first thing that comes to mind is a producer-consumer scenario using a buffer.

Three Components in our system:

  1. Queue [Buffer] - A fixed-size queue shared between threads
  2. Producer - A thread produces/inserts values to the buffer
  3. Consumer - A thread consumes/removes values from the buffer

PRODUCER THREAD: The producer inserts values in the buffer and until the buffer is full. If the buffer is full, the producer call wait() and enters the wait stage, until the consumer awakes it.

    static 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) {
                if (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.notify();
            }
            sleepRandom();
        }
    }
}

CONSUMER THREAD: Consumer thread removes value from the buffer until the buffer is empty. If the buffer is empty, consumer calls wait() method and enter wait state until a producer sends a notify signal.

    static 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() {
        Random random = new Random();
        while (true) {
            synchronized (queue) {
                if (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(" vvv Consuming value : " + queue.remove());
                queue.notify();
            }
            sleepRandom();
        }
    }
}

UTIL METHOD:

    public static void sleepRandom(){
    Random random = new Random();
    try {
        Thread.sleep(random.nextInt(250));
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

Application Code:

    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();
}

A Sample Output:

 ^^^ Producing value : 1268801606
 vvv Consuming value : 1268801606
Queue is empty,Consumer thread is waiting for producer thread to put something in queue
 ^^^ Producing value : -191710046
 vvv Consuming value : -191710046
 ^^^ Producing value : -1096119803
 vvv Consuming value : -1096119803
 ^^^ Producing value : -1502054254
 vvv Consuming value : -1502054254
Queue is empty,Consumer thread is waiting for producer thread to put something in queue
 ^^^ Producing value : 408960851
 vvv Consuming value : 408960851
 ^^^ Producing value : 2140469519
 vvv Consuming value : 65361724
 ^^^ Producing value : 1844915867
 ^^^ Producing value : 1551384069
 ^^^ Producing value : -2112162412
 vvv Consuming value : -887946831
 vvv Consuming value : 1427122528
 ^^^ Producing value : -181736500
 ^^^ Producing value : -1603239584
 ^^^ Producing value : 175404355
 vvv Consuming value : 1356483172
 ^^^ Producing value : -1505603127
 vvv Consuming value : 267333829
 ^^^ Producing value : 1986055041
Queue is full, Producer thread waiting for consumer to take something from queue
 vvv Consuming value : -1289385327
 ^^^ Producing value : 58340504
 vvv Consuming value : 1244183136
 ^^^ Producing value : 1582191907
Queue is full, Producer thread waiting for consumer to take something from queue
 vvv Consuming value : 1401174346
 ^^^ Producing value : 1617821198
 vvv Consuming value : -1827889861
 vvv Consuming value : 2098088641

Upvotes: 1

Ferhat KO&#199;ER
Ferhat KO&#199;ER

Reputation: 4075

Example

public class myThread extends Thread{
     @override
     public void run(){
        while(true){
           threadCondWait();// Circle waiting...
           //bla bla bla bla
        }
     }
     public synchronized void threadCondWait(){
        while(myCondition){
           wait();//Comminucate with notify()
        }
     }

}
public class myAnotherThread extends Thread{
     @override
     public void run(){
        //Bla Bla bla
        notify();//Trigger wait() Next Step
     }

}

Upvotes: 2

srinivas
srinivas

Reputation: 472

Example for wait() and notifyall() in Threading.

A synchronized static array list is used as resource and wait() method is called if the array list is empty. notify() method is invoked once a element is added for the array list.

public class PrinterResource extends Thread{

//resource
public static List<String> arrayList = new ArrayList<String>();

public void addElement(String a){
    //System.out.println("Add element method "+this.getName());
    synchronized (arrayList) {
        arrayList.add(a);
        arrayList.notifyAll();
    }
}

public void removeElement(){
    //System.out.println("Remove element method  "+this.getName());
    synchronized (arrayList) {
        if(arrayList.size() == 0){
            try {
                arrayList.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }else{
            arrayList.remove(0);
        }
    }
}

public void run(){
    System.out.println("Thread name -- "+this.getName());
    if(!this.getName().equalsIgnoreCase("p4")){
        this.removeElement();
    }
    this.addElement("threads");

}

public static void main(String[] args) {
    PrinterResource p1 = new PrinterResource();
    p1.setName("p1");
    p1.start();

    PrinterResource p2 = new PrinterResource();
    p2.setName("p2");
    p2.start();


    PrinterResource p3 = new PrinterResource();
    p3.setName("p3");
    p3.start();


    PrinterResource p4 = new PrinterResource();
    p4.setName("p4");
    p4.start();     

    try{
        p1.join();
        p2.join();
        p3.join();
        p4.join();
    }catch(InterruptedException e){
        e.printStackTrace();
    }
    System.out.println("Final size of arraylist  "+arrayList.size());
   }
}

Upvotes: -1

Jared Russell
Jared Russell

Reputation: 11342

The wait() and notify() methods are designed to provide a mechanism to allow a thread to block until a specific condition is met. For this I assume you're wanting to write a blocking queue implementation, where you have some fixed size backing-store of elements.

The first thing you have to do is to identify the conditions that you want the methods to wait for. In this case, you will want the put() method to block until there is free space in the store, and you will want the take() method to block until there is some element to return.

public class BlockingQueue<T> {

    private Queue<T> queue = new LinkedList<T>();
    private int capacity;

    public BlockingQueue(int capacity) {
        this.capacity = capacity;
    }

    public synchronized void put(T element) throws InterruptedException {
        while(queue.size() == capacity) {
            wait();
        }

        queue.add(element);
        notify(); // notifyAll() for multiple producer/consumer threads
    }

    public synchronized T take() throws InterruptedException {
        while(queue.isEmpty()) {
            wait();
        }

        T item = queue.remove();
        notify(); // notifyAll() for multiple producer/consumer threads
        return item;
    }
}

There are a few things to note about the way in which you must use the wait and notify mechanisms.

Firstly, you need to ensure that any calls to wait() or notify() are within a synchronized region of code (with the wait() and notify() calls being synchronized on the same object). The reason for this (other than the standard thread safety concerns) is due to something known as a missed signal.

An example of this, is that a thread may call put() when the queue happens to be full, it then checks the condition, sees that the queue is full, however before it can block another thread is scheduled. This second thread then take()'s an element from the queue, and notifies the waiting threads that the queue is no longer full. Because the first thread has already checked the condition however, it will simply call wait() after being re-scheduled, even though it could make progress.

By synchronizing on a shared object, you can ensure that this problem does not occur, as the second thread's take() call will not be able to make progress until the first thread has actually blocked.

Secondly, you need to put the condition you are checking in a while loop, rather than an if statement, due to a problem known as spurious wake-ups. This is where a waiting thread can sometimes be re-activated without notify() being called. Putting this check in a while loop will ensure that if a spurious wake-up occurs, the condition will be re-checked, and the thread will call wait() again.


As some of the other answers have mentioned, Java 1.5 introduced a new concurrency library (in the java.util.concurrent package) which was designed to provide a higher level abstraction over the wait/notify mechanism. Using these new features, you could rewrite the original example like so:

public class BlockingQueue<T> {

    private Queue<T> queue = new LinkedList<T>();
    private int capacity;
    private Lock lock = new ReentrantLock();
    private Condition notFull = lock.newCondition();
    private Condition notEmpty = lock.newCondition();

    public BlockingQueue(int capacity) {
        this.capacity = capacity;
    }

    public void put(T element) throws InterruptedException {
        lock.lock();
        try {
            while(queue.size() == capacity) {
                notFull.await();
            }

            queue.add(element);
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
    }

    public T take() throws InterruptedException {
        lock.lock();
        try {
            while(queue.isEmpty()) {
                notEmpty.await();
            }

            T item = queue.remove();
            notFull.signal();
            return item;
        } finally {
            lock.unlock();
        }
    }
}

Of course if you actually need a blocking queue, then you should use an implementation of the BlockingQueue interface.

Also, for stuff like this I'd highly recommend Java Concurrency in Practice, as it covers everything you could want to know about concurrency related problems and solutions.

Upvotes: 305

extraneon
extraneon

Reputation: 23950

Have you taken a look at this Java Tutorial?

Further, I'd advise you to stay the heck away from playing with this kind of stuff in real software. It's good to play with it so you know what it is, but concurrency has pitfalls all over the place. It's better to use higher level abstractions and synchronized collections or JMS queues if you are building software for other people.

That is at least what I do. I'm not a concurrency expert so I stay away from handling threads by hand wherever possible.

Upvotes: 10

Enno Shioji
Enno Shioji

Reputation: 26882

Not a queue example, but extremely simple :)

class MyHouse {
    private boolean pizzaArrived = false;

    public void eatPizza(){
        synchronized(this){
            while(!pizzaArrived){
                wait();
            }
        }
        System.out.println("yumyum..");
    }

    public void pizzaGuy(){
        synchronized(this){
             this.pizzaArrived = true;
             notifyAll();
        }
    }
}

Some important points:
1) NEVER do

 if(!pizzaArrived){
     wait();
 }

Always use while(condition), because

  • a) threads can sporadically awake from waiting state without being notified by anyone. (even when the pizza guy didn't ring the chime, somebody would decide try eating the pizza.).
  • b) You should check for the condition again after acquiring the synchronized lock. Let's say pizza don't last forever. You awake, line-up for the pizza, but it's not enough for everybody. If you don't check, you might eat paper! :) (probably better example would be while(!pizzaExists){ wait(); }.

2) You must hold the lock (synchronized) before invoking wait/nofity. Threads also have to acquire lock before waking.

3) Try to avoid acquiring any lock within your synchronized block and strive to not invoke alien methods (methods you don't know for sure what they are doing). If you have to, make sure to take measures to avoid deadlocks.

4) Be careful with notify(). Stick with notifyAll() until you know what you are doing.

5)Last, but not least, read Java Concurrency in Practice!

Upvotes: 170

Related Questions