187

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

Olaseni
  • 7,250
  • 15
  • 41
  • 64

6 Answers6

278

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.

Elmer
  • 669
  • 6
  • 14
Jared Russell
  • 9,149
  • 6
  • 27
  • 29
  • @finnw are you sure it will cause deadlock? In which scenario? For me, Jared's code seems to be correct. – greuze Feb 17 '13 at 22:06
  • 7
    @greuze, `notify` wakes only one thread. If two consumer threads are competing to remove an element, one notify may wake the other consumer thread, which cannot do anything about it and will go back to sleep (instead of the producer, which we were hoping would insert a new element.) Because the producer thread is not woken, nothing gets inserted and now all three threads will sleep indefinitely. I removed my previous comment as it said (wrongly) that spurious wakeup was the cause of the problem (It is not.) – finnw Feb 17 '13 at 22:18
  • Thanks for the explanation @finnw I think you are right, now I realize. I think this problem could be solved if we use two different objects for wait/notify (similar to second solution with concurrent package) – greuze Feb 19 '13 at 12:02
  • 1
    @finnw As far as I can tell, the problem that you have spotted can be solved by using notifyAll(). Am I right? – Clint Eastwood Aug 04 '13 at 14:16
  • As per Oracle concurrency tutorial referring to notifyAll(): "Note: There is a second notification method, notify, which wakes up a single thread. Because notify doesn't allow you to specify the thread that is woken up, it is useful only in massively parallel applications — that is, programs with a large number of threads, all doing similar chores. In such an application, you don't care which thread gets woken up." – Rui Marques Mar 28 '14 at 14:26
  • @Jared is it possible to synchronize while block instead of whole method ? – Navrattan Yadav Dec 08 '15 at 15:35
  • 1
    The example given here by @Jared is pretty good but has a serious fall. In the code all the methods have been marked as synchronized, but NO TWO SYNCHRONIZED METHODS CAN BE EXECUTED AT THE SAME TIME, then how come there is second thread in the picture. – Shivam Aggarwal Dec 13 '15 at 13:14
  • 10
    @Brut3Forc3 you need to read the javadoc of wait(): it says: *The thread releases ownership of this monitor*. So, as soon as wait() is called, the monitor is released, and another thread can execute another synchronized method of the queue. – JB Nizet Dec 13 '15 at 15:57
  • 1
    @JBNizet. " 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 ” . Here how come the second thread is scheduled if wait has not yet been called . – Shivam Aggarwal Dec 13 '15 at 16:17
  • 1
    @Brut3Forc3 Re-read the post. At this time, Jared explains what could happen if the method was NOT synchronized. – JB Nizet Dec 13 '15 at 16:37
  • In the second *T take()* a return-statement is missing at the end of function. It's not sufficient to provide the return in the try-block only, isn't it ? – user1511417 Feb 26 '16 at 14:51
  • about the while loop,the await() function will be called and called,it isn't lead to performance issues?or the await() implementation know how to handle this? – yarin Apr 30 '16 at 08:47
  • just a thought...is double checked locking mechanism not possible? That way, you synchronize only the critical section... – Yogesh Ghimire Jun 08 '18 at 15:56
  • The second word in question was `simple`! – Zon Oct 11 '18 at 19:27
  • @Jared, in the first code example, about the two `while` blocks, I think we can replace them with the `if` and it also works. Is it right? – Messi Jun 21 '19 at 01:28
  • @Messi Not really, because you want to recheck the condition and wait again if it is true when the thread spuriously wakes up. – mrida Sep 13 '19 at 14:54
152

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!

Enno Shioji
  • 25,422
  • 13
  • 67
  • 104
  • 1
    Could you please elaborate on why not to use "if( ! pizzaArrived ){ wait(); } " ? – Everyone Mar 29 '10 at 11:21
  • 2
    @Everyone: Added some explanation. HTH. – Enno Shioji Mar 29 '10 at 18:05
  • 1
    why using the `pizzaArrived`flag? if the flag is changed without a call to `notify` it won't have any effect. Also just with `wait` and `notify` calls the example works. – Pablo Fernandez Dec 01 '12 at 18:22
  • @PabloFernandez: Nope, see http://stackoverflow.com/questions/2540984/why-do-threads-spontaneously-awake-from-wait (and my explanation in the answer) – Enno Shioji Dec 01 '12 at 18:54
  • 2
    I don't understand - thread 1 executes the eatPizza() method and enters the top synchronized block, and synchronizes on the MyHouse class. No pizza has yet arrived so it just waits. Now thread 2 tries to deliver the pizza by calling the pizzaGuy() method; but can't as thread 1 already owns the lock and it's not giving it up (it's perpetually waiting). Effectively the result is a deadlock - thread 1 is waiting for thread 2 to execute the notifyAll() method, while thread 2 is waiting for thread 1 to give up the lock on the MyHouse class... What is it that I'm missing here? – flamming_python Feb 04 '14 at 22:37
  • Right so I checked - the wait method causes ownership of the lock to be given up then? That would make sense. – flamming_python Feb 04 '14 at 22:44
  • Can you please elaborate on stick with notifyAll part – Sumit Jain May 01 '14 at 06:59
  • @SumitJain: see http://stackoverflow.com/questions/37026/java-notify-vs-notifyall-all-over-again – Enno Shioji May 01 '14 at 08:09
  • I really like your idea of using pizza for the example. Much better than a variable named *foo*. Can I borrow this example for [a Q&A](http://meta.stackoverflow.com/questions/269174/questions-about-threadloop-not-working-without-print-statement) I'm writing? – Boann Aug 20 '14 at 11:36
  • How to use your class to see effect ? – Ravi Jul 17 '15 at 04:21
  • Good example. Test client : class MyHouse { private boolean pizzaArrived = false; public void eatPizza(){ synchronized(this){ while(!pizzaArrived){ System.out.println("waiting for pizza.."); wait(); } } System.out.println("yumyum.."); } public void deliverPizza(){ synchronized(this){ System.out.println("Please wait..stuck in traffic"); Thread.sleep(2000); this.pizzaArrived = true; notifyAll(); } } } – supernova Dec 30 '17 at 02:32
  • Test Caller class : --- public class OrderPizza { public static void main(String[] args) { MyHouse home = new MyHouse(); Runnable owner = new Runnable(){ public void run(){ home.eatPizza(); } }; Runnable pizzaGuy = new Runnable(){ public void run(){ home.deliverPizza(); } }; new Thread(owner).start(); new Thread(pizzaGuy).start(); } } – supernova Dec 30 '17 at 02:39
  • shouldn't pizzaArrived variable be volatile ? – mrida Sep 13 '19 at 14:37
  • 1
    No, when a variable is guarded by `synchronized` keyword, it is redundant to declare the variable `volatile`, and it is recommended to avoid it to avoid confusion @mrida – Enno Shioji Sep 15 '19 at 08:20
40

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.

polygenelubricants
  • 348,637
  • 121
  • 546
  • 611
  • The BlockingQueueS provided in the java.util.concurrent package aren't persistent. What can we use when the queue needs to be persistent? i.e. if the system goes down with 20 items in the queue I need those to be present when the system restarts. As the java.util.concurrent queues all appear to be 'in memory' only is there any way these could be used as is/hacked/overridden to provide implementations that are persistence capable? – Volksman May 06 '13 at 06:01
  • 1
    Perhaps the backing queue could be provided? i.e. we would provide a Queue interface implementation that is persistent. – Volksman May 06 '13 at 06:02
  • This is very good to mentioned in this context that you would never need to use the `notify()` and the `wait()` again – Arefe Feb 01 '18 at 03:22
8

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.

Simon
  • 3,962
  • 7
  • 26
  • 51
extraneon
  • 22,016
  • 2
  • 42
  • 49
2

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
     }

}
Farid Chowdhury
  • 1,163
  • 13
  • 13
Ferhat KOÇER
  • 2,942
  • 20
  • 23
0

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());
   }
}
srinivas
  • 444
  • 6
  • 14