0

I'm trying to solve single consumer/producer problem using monitor in Java, and the code is as follows. When I run this code, it will finally get stucked. The most typical case is that the consumer calls wait(), and then the producer keeps producing but cannot notify the consumer (although it will call notify()). I don't know why it's happening. Java code:

import java.util.*;
class Monitor {
    int length;
    int size;
    int begin, end;
    int queue[];
    private static Random randGenerator;
    public Monitor() {}
    public Monitor(int length) {
        this.length = length;
        this.size = 0;
        begin = end = 0;
        queue = new int[length];
        randGenerator = new Random(10);
    }
    public synchronized void produce() throws InterruptedException {
        while(size == length) {
            System.out.println("Producer waiting");
            wait();
        }
        int produced = randGenerator.nextInt();
        size++;
        queue[end] = produced;
        end = (end + 1) % length;
        System.out.println("Produce element " + produced + " size "+size);
        // When size is not 1, no thread is blocked and therefore don't need to notify
        if(size == 1) {
            System.out.println("Notify consumer");
            notify();
        }
    }
    public synchronized void consume() throws InterruptedException {
        while(size == 0) {
            System.out.println("Consumer waiting, size " + size);
            wait();
        }
        size--;
        System.out.println("Consume element " + queue[begin] + " size " + size);
        begin = (begin + 1) % length;
        if(size == length - 1) {
            System.out.println("Notify producer");
            notify();
        }
    }
}

class Producer implements Runnable {
    Monitor producer;
    public Producer(Monitor m) {
        producer = m;
    }
    @Override
    public void run() {
        producer = new Monitor();
        System.out.println("Producer created");
        try {
            while(true) {
                producer.produce();
            }
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class Consumer implements Runnable {
    Monitor consumer;
    public Consumer(Monitor m) {
        consumer = m;
    }
    @Override
    public void run() {
        System.out.println("Consumer created");
        consumer = new Monitor();
        try {
            while(true) {
                consumer.consume();
            }
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class monitorTest {
    public static void main(String args[]) {
        Monitor monitor = new Monitor(10);
        Thread t1 = new Thread(new Producer(monitor));
        Thread t2 = new Thread(new Consumer(monitor));
        t1.start();
        t2.start();
    }
}
Harold H.
  • 57
  • 5
  • Suppose, 2 consumers come first, both enter `wait()`. Then a producer comes, issues one `notify()`. Immediately afterwards, before any consumer is woken, another producer comes, adds an item, but without any notifications. Now, only one consumer will be woken. – ZhongYu Feb 15 '17 at 05:19
  • @ZhongYu Thanks for your comment. I create only one producer and one consumer in main function, and the program still cannot run normally. Why is that? Note that I have mentioned the program is targeted for single producer/consumer problem. – Harold H. Feb 15 '17 at 05:23
  • You should not have called `new Monitor()` again - just use the monitor created in main() – ZhongYu Feb 15 '17 at 05:27
  • @ZhongYu After deleting these two lines it works fine. Thanks!!! (what a silly mistake for me lol). Since you didn't post an answer (no need indeed...), I'll mark anacron's answer as accepted. – Harold H. Feb 15 '17 at 05:40

1 Answers1

1

When the control of each thread enters the produce() or consume() methods, the size and length are both zero and hence both threads are waiting for the other to notify. Break this and your code will come out of the deadlock.

public synchronized void produce() throws InterruptedException {
    while(size == length) { // size is 0 and length is 0; so wait
        System.out.println("Producer waiting");
        wait();
    }

public synchronized void consume() throws InterruptedException {
    while(size == 0) { // size is 0 so wait
        System.out.println("Consumer waiting, size " + size);
        wait();
    }

This is happening because you have a default constructor which you are calling inside the run() method of your Producer and Consumer objects.

class Producer implements Runnable {
    Monitor producer;
    public Producer(Monitor m) {
        producer = m;
    }
    @Override
    public void run() {
        producer = new Monitor(); // REMOVE THIS

class Consumer implements Runnable {
    Monitor consumer;
    public Consumer(Monitor m) {
        consumer = m;
    }
    @Override
    public void run() {
        System.out.println("Consumer created");
        consumer = new Monitor(); // AND REMOVE THIS

Hope this helps!

anacron
  • 6,443
  • 2
  • 26
  • 31
  • But I do initialize monitor.length to be 10(`Monitor monitor = new Monitor(10)`), so length should not be 0 but 10. Am I wrong? – Harold H. Feb 15 '17 at 05:27
  • I printed the size and length before the beginning of the `while` statement. I got size = 0 and length = 0. – anacron Feb 15 '17 at 05:28