Wait and Notify

So, returning to interview questions: wait and notify.
What are they?
They are methods that are inherited from the Object class in Java. Actually, we will review not notify but notifyAll, because this is the one more useful for practical purposes.

So, what does wait() do?
Well, it waits.
Meaning that the current thread becomes suspended waiting on some event.
How does it know that this event happened?
Why, that is where notify() or notifyAll() comes into play.
The difference between using wait() and a simple loop checking on some condition is critical here. Actually, we will have to use the condition anyway, but what wait() does is very important. It releases the lock on the object and so yields the object to other threads that might want to acquire the lock. If it was just some loop with no wait(), the thread would just continue its execution without giving anyone a chance to change the object state and therefore make that condition true. 

With wait() yielding the object lock, those other threads can change the object state and call notify() to signal for the waiting thread that the state has changed and therefore it can check that condition again. That is the point where the first thread returns its control over the object and the wait() method returns.

But why have that condition still?
Well, because we can’t just assume that the notify() was called because the state changed the way we want it. A lot of things can happen in a program, and it might not be necessarily the thing we waited for.

Also, wait() has better be called from a syncronized method, because in order to release the lock, the thread must first acquire it. The easiest way to do it is use a syncronized method to ensure the thread owns the object lock.

So, let us have a simple example of how wait() and notify() works. First of all, we’ll have a Waiter. The waiter can only handle one order at any moment of time. He can’t accept new orders until previous ones had been delivered; he also can’t deliver orders until they have been made.

    public class Waiter {
    private boolean hasOrder;
    private Order order;

    public synchronized void takeOrder(String order) {
        while (hasOrder) {
            try {
                wait();
            } catch (InterruptedException e) {
                // do nothing
            }
        }
        hasOrder = true;
        System.out.println("Accepting order: " + order);
        this.order = new Order(order);
        notifyAll();
    }

    public synchronized void deliverOrder() {
        while (!hasOrder) {
            try {
                wait();
            } catch (InterruptedException e) {
                // do nothing
            }
        }
        hasOrder = false;
        System.out.println("Delivering order: " + order.getOrder() + ", time: " + (System.currentTimeMillis()));
        this.order = null;
        notifyAll();
    }

    public static class Order {
        private String order;

        public Order(String order) {
            this.order = order;
        }

        public String getOrder() {
            return order;
        }

        public void setOrder(String order) {
            this.order = order;
        }

        @Override
        public String toString() {
            return "Order{" + order + '}';
        }
    }
}

We will have two threads, the Client and the Chef. The Client will order things and the Chef will prepare them and call the Waiter to make the delivery. We will randomize the process so that it would not happen in any specific order, that is, we want the Client to order things before previous orders have been ready. We also want a crazy Chef who might try to deliver things before they are even ordered.

    public class Runner {
    public static void main(String args[]) {

        final Waiter waiter = new Waiter();
        final String[] orders = new String[]{"1 - Juice", "2 - Eggs", "3 - Toast", "4 - Coffee"};
        final Random random = new Random();
        final long start = System.currentTimeMillis();

        Thread client = new Thread(new Runnable() {
            int orderCount = 0;
            @Override
            public void run() {
                while (orderCount < orders.length) {
                    System.out.println("\nInitiating order #" + (orderCount + 1) + ". Time: " + (System.currentTimeMillis() - start));
                    waiter.takeOrder(orders[orderCount++]);
                    try {
                        Thread.sleep(random.nextInt(5000));
                    } catch (InterruptedException e) {
                        // ignore
                    }
                }
            }
        });
        Thread chef = new Thread(new Runnable() {
            int orderCount = 0;
            @Override
            public void run() {
                while (orderCount < orders.length) {
                    System.out.println("Initiating delivery #" + (orderCount + 1) + ". Time: " + (System.currentTimeMillis() - start));
                    waiter.deliverOrder();
                    orderCount++;
                    try {
                        Thread.sleep(random.nextInt(5000));
                    } catch (InterruptedException e) {
                        // ignore
                    }
                }
            }
        });
        client.start();
        chef.start();
    }
}

So, what happens when we run this code?

The execution is randomised, but this is one of the examples:


Initiating order #1. Time: 2
Initiating delivery #1. Time: 2
Accepting order: 1 - Juice
Delivering order: 1 - Juice
Initiating delivery #2. Time: 365

Initiating order #2. Time: 704
Accepting order: 2 - Eggs
Delivering order: 2 - Eggs
Initiating delivery #3. Time: 3840

Initiating order #3. Time: 4531
Accepting order: 3 - Toast
Delivering order: 3 - Toast
Initiating delivery #4. Time: 6515

Initiating order #4. Time: 8376
Accepting order: 4 - Coffee
Delivering order: 4 - Coffee

So what do we see?

Our Chef is indeed pretty crazy, our Client is not much better. Still, all the deliveries happen in order and we have all orders completed!

Advertisements

About Maryna Cherniavska

I have productively spent 10+ years in IT industry, designing, developing, building and deploying desktop and web applications, designing database structures and otherwise proving that females have a place among software developers. And this is a good place.
This entry was posted in Uncategorized. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s