Search code examples
javamultithreadingsynchronizationproducer-consumer

One thread does not run till the other one is ended


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?


Solution

  • 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.