Search code examples
javamultithreadingwaitproducer-consumercontext-switch

can waiting thread which woke up context switch again


I am trying to get my Producer/Consumer working and I deliberately don't want to use BlockingQueue to understand finer details here. I understand when I call object.wait() the thread looses its lock and goes to WAITING state till someone notifies (notify/notifyAll) which beings it back to BLOCKED state and this thread would go to RUNNABLE if it acquires the lock.

    private class Consumer implements Runnable{
        private final MyQueue<Integer> queue;
        public Consumer(MyQueue<Integer> queue){
            this.queue = queue;
        }
        @Override
        public void run(){
            while(true){
                synchronized (queue) {
                    //Block till new available
                    while(queue.isEmpty()){
                        try {
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    //Consume
                    queue.dequeue();
                    queue.notifyAll(); //Notify people waiting on queue full condition
                }
            }
        }
    }

My question is it possible that (with respect to code above):

  1. My thread was waiting and woke up after wait (some one notified) and i got the lock
  2. I made a check queue.isEmpty() and it was not empty and execution goes to next line
  3. Just before the next line queue.dequeue() is executed CPU context switched my thread.
  4. Next when i get my cpu slice and lock, i run queue.dequeue() and say queue is empty

I know a usual CPU scheduler gives a quantum of time to each thread to avoid the context switching costs.


Solution

  • That situation won't happen, precisely because any modification to the queue is (or at least should be) done from a synchronized block on the queue. So, no other thread can modify the queue between then end of the while loop and the call to dequeue(), since your thread is the one holding the lock.

    Of course, if some other consumer also removes elements from the queue without synchronizing on the queue, you will have this problem. But that would simply be a bug in your code. Using encapsulation is the best way to avoid those kinds of bugs. If you used a BlockingQueue, the BlockingQueue class would encapsulate the exclusive access to the shared state, and you wouldn't have to make sure every access to the queue is properly synchronized, since the BlockingQueue would do it for you.