Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unexpected result return when implementing wait and notify

I've made a wait and notify example program that is abstraction of a cake shop. there is a thread with role cake machine for producing cake and a thread with role waiter for delivering cake. My expectation is each time the CakeMachine class completes producing a cake it will send a notify to the Waiter class for delivering. When I run a program which produce 3 cake, the result shows that only one cake is delivered. Here is my code:

The Cake class for creating cake object:

class Cake {
private int weight;
private String color;

public Cake(int weight, String color) {
    this.weight = weight;
    this.color = color;
    }

public String toString(){
    return "The cake is in " + color  +" and is " + weight + " gram ";

    }
}

The CakeMachine class for producing cake:

class CakeMachine implements Runnable{


private List<Cake> listCake;

CakeMachine(List<Cake> listCake) {
    this.listCake = listCake;
}

public void  makeCake() {
        int weight = new Random().nextInt(20);
        Cake cake = new Cake(weight, "color code is  " + weight );
        listCake.add(cake);
        System.out.println("cake has been cooked ");
        listCake.notify();
}

@Override
public void run() {

        for (int i = 0; i < 3; i++) {
           synchronized (listCake) {
               makeCake();
           }
        }
    }
}

The Waiter class for delivering cake:

class Waiter implements Runnable {

private List<Cake> listCake;

Waiter(List<Cake> listCake) {
    this.listCake = listCake;
}

public void delivery() {
        System.out.println("Waiter is waiting for the cake ");

            try {
                listCake.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
         Cake cake = listCake.get(listCake.size() - 1);
         System.out.println(cake.toString() + "has been delivered to customers ");
}

@Override
public void run() {

    for (int i = 0; i < 3; i++)
    {
        synchronized (listCake) {
            delivery();
        }
    }
  }
}

And the main class:

public class WaitAndNotify {

    public static List<Cake> listCake = new ArrayList<>();

    public static void main(String[] args) {

    Thread waiter = new Thread(new Waiter(listCake));
    Thread cakeMachine = new Thread(new CakeMachine(listCake));
    waiter.start();
    cakeMachine.start();

    }
}

The result when run the program is:

Waiter is waiting for the cake 
cake has been cooked 
cake has been cooked 
cake has been cooked 
The cake is in color code is  18 and is 18 gram has been delivered to   customers 
Waiter is waiting for the cake 

Please help me to understand this situation.

like image 229
programer310 Avatar asked Feb 10 '26 06:02

programer310


2 Answers

Two major points:

  • You don't have control over which thread runs when.

  • notify has no effect if no other thread happens to be waiting on that monitor. Your waiter stops waiting and the cake-making is free to proceed, and finishes before the waiter acquires the monitor again.

In your example the scheduler decides to run one iteration of your waiter first, then runs all 3 iterations of cake-making, then runs the next two iterations of waiting, after all the cakes have been made. You could introduce flags to indicate when a cake was prepared and when a waiter was waiting, and make the cake-making delay notifying until a waiter was present, but it's really a mistake to try to get threads to execute in lockstep, it defeats the purpose of separating activities into their own threads.

With the condition flags added the delivery method would look something like:

public void delivery() {
    System.out.println("Waiter is waiting for the cake ");
    waiterInPosition = true;
    try {
        while (!(cakeQueued)) {
            listCake.wait();
        }
        waiterInPosition = false;
        cakeQueued = false;
        Cake cake = listCake.get(listCake.size() - 1);
        System.out.println(cake.toString() 
        + "has been delivered to customers ");
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt(); // restore interrupt flag
    }
}

But this is a toy example and it is very awkward. It would be more natural to model this as a producer-consumer problem where the cake-maker added cakes to a queue and the waiter took cakes off the queue. You can try making your own blocking queue, your queue methods can handle the waiting and notifying -- it's a lot more natural to have your data structure handle synchronization and blocking than it is to make the worker tasks do it. That will teach you as much about using wait and notify as your current example, while being more representative of real-world concurrency patterns.

The Oracle tutorial on guarded blocks includes a blocking queue example, and the advice about waiting in a loop with a condition variable is essential, go read it.

like image 126
Nathan Hughes Avatar answered Feb 14 '26 17:02

Nathan Hughes


This can be solved with Typical producer consumer problem

The Cake Class:

class Cake {
private int weight;
private String color;

public Cake(int weight, String color) {
    this.weight = weight;
    this.color = color;
}

public String toString() {
    return "The cake is in " + color + " and is " + weight + " gram ";

}
}

CakeMachine acts as both producer and consumer that is it has both delivercake and make cake function

class CakeMachine {

private LinkedList<Cake> listCake = new LinkedList<Cake>();;
private Random random = new Random();
private static int LIMIT = 5;

public void makeCake() throws InterruptedException {
    while (true) {
        synchronized (this) {
            while (listCake.size() == LIMIT) {
                wait();
            }
            int weight = random.nextInt(20);
            Cake cake = new Cake(weight, "color code is  " + weight);
            listCake.add(cake);
            System.out.println("cake has been cooked ");
            notify();
        }
    }
}

public void deliverCake() throws InterruptedException {
    while (true) {
        synchronized (this) {
            Thread.sleep(random.nextInt(1000)); // on average 500 ms
            while (listCake.size() == 0) {
                System.out.println("Waiter is waiting for the cake ");
                wait();
            }

            Cake cake = listCake.removeFirst();
            System.out.println(cake.toString() + "has been delivered to customers ");
            notify();
        }
    }
}

}

Now the final and main class

public class CakeThreading {

public static void main(String[] args) {

    final CakeMachine cm = new CakeMachine();
    Thread cakeproducer = new Thread(new Runnable() {

        @Override
        public void run() {
            try {
                cm.makeCake();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    });

    Thread waiter = new Thread(new Runnable() {

        @Override
        public void run() {
            try {
                cm.deliverCake();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    });

    cakeproducer.start();
    waiter.start();

    try {
        cakeproducer.join();
        waiter.join();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
}

This will produce an output like this:

cake has been cooked 
cake has been cooked 
The cake is in color code is  2 and is 2 gram has been delivered to customers 
The cake is in color code is  14 and is 14 gram has been delivered to customers 
cake has been cooked 
cake has been cooked 
cake has been cooked 
The cake is in color code is  13 and is 13 gram has been delivered to customers 
The cake is in color code is  7 and is 7 gram has been delivered to customers 
The cake is in color code is  19 and is 19 gram has been delivered to customers 
Waiter is waiting for the cake 
cake has been cooked 
cake has been cooked 
cake has been cooked 
The cake is in color code is  12 and is 12 gram has been delivered to customers 
The cake is in color code is  2 and is 2 gram has been delivered to customers 
The cake is in color code is  2 and is 2 gram has been delivered to customers 
cake has been cooked 
cake has been cooked 
like image 23
ankit249 Avatar answered Feb 14 '26 18:02

ankit249



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!