0

I am making a simple producer consumer problem. The waiter is the producer and the chef is the consumer. I have implemented it such that the Chef thread waits till Waiter notifies it of an incoming order in the Queue.

public class Main {

    public static void main(String args[]) {
        final Queue<Integer> sharedQ = new LinkedList<Integer>();
        Thread waiter = new Waiter(sharedQ);
        Thread chef = new Chef(sharedQ);
        waiter.start();
        chef.start();
    }
}

class Items{
    String[] items = {" ", "Sandwich",  "Cereal", "Coffee", "Pizza"};
    int[] time = {0,5,3,3,7};

    String getItem(int value){
        return items[value];
    }

    int getTime(int value){
        return time[value];
    }
}

class Waiter extends Thread{
    private final Queue<Integer> sharedQ;
    static int ord = 0;
    Items item = new Items();
    SimpleDateFormat dateFormat = new SimpleDateFormat("hh:mm:ss aa");
    public Waiter(Queue<Integer> sharedQ) {
        super("Waiter");
        this.sharedQ = sharedQ;
    }

    @Override
    public void run() {
        int choice = 1;
        Scanner in = new Scanner(System.in);
        while(choice >= 1 && choice <= 4) {
            synchronized (sharedQ) {
                System.out.print("Enter item id :");
                choice = in.nextInt();
                if(choice >= 1 && choice <= 4){
                    ord++;
                    System.out.println("Order Number: ORD"+ord+" for " + item.getItem(choice)+" has been placed at +"+dateFormat.format(new Date()).toString());
                    sharedQ.add(choice);
                    sharedQ.notifyAll();
                }
            }
        }
    }
}

class Chef extends Thread{
    private final Queue<Integer> sharedQ;
    Items item = new Items();
    static int ord = 0;
    SimpleDateFormat dateFormat = new SimpleDateFormat("hh:mm:ss aa");
    public Chef(Queue<Integer> sharedQ) {
        super("Chef");
        this.sharedQ = sharedQ;
    }

    @Override
    public void run() {
        int choice;
        while(true) {
            try{
                synchronized (sharedQ) {
                //waiting condition - wait until Queue is not empty
                    while (sharedQ.size() == 0) {
                        sharedQ.wait();
                    }
                    System.out.println(sharedQ.size());
                    choice = sharedQ.poll();
                    System.out.println("abc");
                    ord++;
                    System.out.println("Chef : Picked up ORD"+ord+" at "+ dateFormat.format(new Date()).toString());
                    System.out.println("Chef: Cooking "+item.getItem(choice));
                    Thread.sleep(60*1000*item.getTime(choice));
                    System.out.println("Chef : Finished making "+item.getItem(choice)+" at "+ dateFormat.format(new Date()).toString());
                    sharedQ.notify();
                }
            }
            catch (InterruptedException ex) {
                System.out.println("Exception in chef function");
            }
        }
    }
}

However, the Chef thread does not respond till the Waiter thread ends. Any leads?

Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
  • 2
    Since `Waiter` is keeping its lock on the shared queue for almost the entire time of its existence, the outcome isn't very surprising. If this is an exercise in multithreading I suggest you try to find a good tutorial about using `wait()/notifyAll()`. If it isn't, just use some kind of blocking queue. – biziclop May 20 '15 at 16:19

2 Answers2

3

The problem is that the Waiter never releases the lock on the shared queue, so the Chef can never read from it.

What would work better is to use a BlockingQueue implementation, like LinkedBlockingQueue, removing the use of synchronized blocks in your program.

For new applications, using intrinsic locks (synchronized blocks), Thread instances, and wait()/notify() calls should be avoided, preferring the use of ExecutorService and the utilities in the java.util.concurrent package.

erickson
  • 265,237
  • 58
  • 395
  • 493
1

Try to move

System.out.print("Enter item id :");
choice = in.nextInt();

out of synchronized section.

Problem occur because Waiter thread spend most of it's time in in.nextInt() method waiting for user input. Lock is acquired all that time. When user enter number and lock is released it almost immediately acquired back for next iteration. Scheduler which decide which thread will get lock next if more then one thread try to get it is not perfect so it is wake up Chef thread but do not acquire lock immediately. And we have situation when both thread try to get lock. If you enter several number you will see that Chef get lock some time, but not always.

talex
  • 17,973
  • 3
  • 29
  • 66
  • can you please explain why it happens.? I notify the Chef as soon as Waiter gets an input. So does that not start executing the Chef thread as well.? – BHARAT KHANNA May 20 '15 at 16:46
  • @BHARATKHANNA This is why I said you should find a tutorial about wait/notify. In a nutshell, `notify()` does absolutely nothing unless there's a matching `wait()` call in some other thread. They can only be used together. – biziclop May 20 '15 at 16:56
  • 1
    @BHARAT: even with the notification, if the waiter still holds the lock the chef can't do anything. read erickson's answer, the synchronization belongs in the data structure, not in the threads accessing the data structure. – Nathan Hughes May 20 '15 at 17:01
  • Thanks a lot talex & @Nathan. I understand it now. Thanks.! – BHARAT KHANNA May 20 '15 at 17:07