Nishant_Singh
Nishant_Singh

Reputation: 798

Producer with multiple consumer failing with notify()

I have this code which is a simulation of Producer Consumer problem using a single producer and multiple consumers on a shared Q class object. I am using notify() here not notifyAll() as i need to understand why this code goes into a deadlock or infinite waiting state.

My Point here is : If there is a single producer and multiple consumers then notify() will invoke only one thread in waiting state and rest will stay in the wait() state . The producer will then resume again and hence the code will keep on executing .

Observation : Here all threads producer and consumers go in an infinite waiting state. The code is shown below :

public class ProdConsProb {
    public static void main(String[] args) {
        Q q = new Q();
        Thread producerThread = new Thread(new Producer(q), "producerThread");
        Thread consumerThread = new Thread(new Consumer(q), "Consumer1");
        Thread consumerAnotherThread = new Thread(new Consumer(q), "Consumer2");
        Thread consumerYetAnotherThread = new Thread(new Consumer(q), "Consumer3");
        producerThread.start();
        consumerThread.start();
        consumerAnotherThread.start();
        consumerYetAnotherThread.start();
    }
}

class Producer implements Runnable {
    Q q;

    public Producer(Q q) {
        this.q = q;
    }

    @Override
    public void run() {
        int i = 0;
        while (true)
            try {
                q.setN(i++);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

    }

}

class Consumer implements Runnable {
    Q q;

    public Consumer(Q q) {
        this.q = q;
    }

    @Override
    public void run() {
        while (true)
            try {
                q.getN();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    }

}

class Q {
    private int n = 0;
    boolean valueSet = false;

    public synchronized int getN() throws InterruptedException {
        while (!valueSet) 
        {
            wait();
        }
        valueSet = false;
        notify();
        return n;
    }

    public synchronized void setN(int n) throws InterruptedException {
        while (valueSet == true)
        {
            wait();
        }
        this.n = n;
        valueSet = true;
        notify();

    }
}

I had added some sysouts . The logs generated are shown below :

producerThread :: SetN : Valueset is false
producerThread :: Producer inserted 0
producerThread :: SetN : Valueset after is true
producerThread :: SetN : Valueset is true
producerThread wait() ------------ Active 
producerThread :: SetN :Wait() Valueset is true
Consumer3  Start :: GetN : Valueset is true
Consumer3 :: Consumer read 0
Consumer3  End :: GetN : Valueset after is false
Consumer3  Start :: GetN : Valueset is false
Consumer3 wait() ------------ Active 
Consumer3 :: GetN :Wait() Valueset is false
Consumer2  Start :: GetN : Valueset is false
Consumer2 wait() ------------ Active 
Consumer2 :: GetN :Wait() Valueset is false
Consumer1  Start :: GetN : Valueset is false
Consumer1 wait() ------------ Active 
Consumer1 :: GetN :Wait() Valueset is false
producerThread wait()   ------------- left 
producerThread :: Producer inserted 1
producerThread :: SetN : Valueset after is true
producerThread :: SetN : Valueset is true
producerThread wait() ------------ Active 
 -->>   producerThread :: SetN :Wait() Valueset is true
        Consumer3 wait() left 
        Consumer3 :: Consumer read 1
        Consumer3  End :: GetN : Valueset after is false
        Consumer3  Start :: GetN : Valueset is false
        Consumer3 wait() ------------ Active 
        Consumer3 :: GetN :Wait() Valueset is false
 ????   Consumer2 wait() left 
        Consumer2 wait() ------------ Active 
        Consumer2 :: GetN :Wait() Valueset is false

The weird thing here was once producer notifies after inserting 1, consumer 3 reads the data and notifies producer. Now producer 3 must trigger back from its wait() but customer2 thread leaves its wait() and goes back to wait() .

Note : This code works with notifyAll() but i am looking for a reason as to why it fails with notify().

Upvotes: 1

Views: 220

Answers (1)

Nathan Hughes
Nathan Hughes

Reputation: 96394

It fails because producers and consumers are waiting on the same monitor and intrinsic locks don’t support separate conditions. If a notification happens either a producer or a consumer can get it. But a given notification will be applicable only to one or the other. When one gets a notification that only the other can act on then the notification doesn’t do any good: the notified thread wakes up, finds the condition it is waiting for is still false, and goes back to waiting.

If you look at ArrayBlockingQueue, it’s implemented with ReentrantLock, with separate conditions, one for consumers and one for producers, so that this kind of bug can’t happen.

Upvotes: 2

Related Questions