2

I am trying to find out whether it is possible to have a multiple producer / multiple consumer queue where I can use notify() instead of notifyAll(). For example, in the implementation below (source: here) you cannot just simply switch the notifyAll() for notify(). It is not totally obvious why you cannot switch so I will leave it as an teaser to whoever wants to help me out understanding this problem.

So the code below is broken:

public class BlockingQueue {

  private Object lock = new Object();

  private List queue = new LinkedList();
  private int  limit = 10;

  public BlockingQueue(int limit){
    this.limit = limit;
  }


  public void enqueue(Object item)
  throws InterruptedException  {
   synchronized(lock) {
    while(this.queue.size() == this.limit) {
      lock.wait();
    }
    if(this.queue.size() == 0) {
      lock.notify();
    }
    this.queue.add(item);
   }
  }


  public Object dequeue()
  throws InterruptedException{
   synchronized(lock) {
    while(this.queue.size() == 0){
      lock.wait();
    }
    if(this.queue.size() == this.limit){
      lock.notify();
    }

    return this.queue.remove(0);
  }
 }
}
2
  • So, in addition for us trying to help you solve your problem, we also have to answer a riddle? Commented Dec 8, 2012 at 5:41
  • I am just trying to make it more interesting, that's all. I edit the question so I make that clear. Commented Dec 8, 2012 at 5:43

3 Answers 3

10

The following steps lead us to deadlock. Let's set limit to 1 to keep the example brief.

  • E1 enqueues an item.
  • E2 attempts enqueue - checks wait loop - already full - waits
  • E3 attempts enqueue - checks wait loop - already full - waits

  • D1 attempts dequeue - and is executing synchronized block

  • D2 attempts dequeue - blocks on entry to the (synchronized) block - due to D1
  • D3 attempts dequeue - blocks on entry to the (synchronized) block - due to D1

  • D1 is executing enqueue - gets the item, calls notify, exits method

  • The notify happens to wake up E2 (i.e. "any waiting thread")
  • BUT, D2 enters sync block before E2 can (E2 must reacquire the lock), so E2 blocks on entry to the enqueue sync block
  • D2 checks wait loop, no more items in queue, so waits
  • D3 enters block after D2, but before E2, checks wait loop, no more items in queue, so waits

  • Now there is E3, D2, and D3 waiting!

  • Finally E2 acquires the lock, enqueues an item, calls notify, exits method

  • E2's notification wakes E3 (remember any thread can be woken)

  • E3 checks the wait loop condition, there is already an item in the queue, so waits.
  • NO MORE THREADS TO CALL NOTIFY and THREE THREADS PERMANENTLY SUSPENDED!

SOLUTION: Replace notify with notifyAll

Sign up to request clarification or add additional context in comments.

7 Comments

So it is impossible to have a blocking queue (multiple consumers and producers) with notify() ?
@chrisapotek That is correct. You can only use notify in cases where only one "type" of thread is involved.
Just wanted to know if it was possible somehow to use notify().
@chrisapotek No problem. As mentioned, it's not possible. Using notify may happen to work (even most of the time), but there is always a chance it could get into the deadlocked state. The example you give is a fairly standard bounded buffer scenario and the general pattern with these is always to use a notifyAll. All patterns like readers/writers, producers/consumers etc. must use notifyAll to avoid deadlock.
OK, what's wrong with a semaphore instead? Waking a dozen threads for one object, then having 11 of them block again almost immediately, does not seem sane to me..
|
0

Whatever your design, know that notify() wakes up a random thread currently waiting on the lock object, and notifyAll() wakes up all such threads (in random order).

As long as your waiting threads are coded to handle the type of wake up you give them, there'll be no problem.

One is not better then the other: In some situations notify() is the best choice, in others notifyAll() - it depends on the problem being tackled.

1 Comment

I wanted to use notify() for a blocking queue, but if you use notify() in the code above it can break the queue. Do you see the situation where it breaks? Is it impossible to use notify() with this type of queue?
0

Move notify out of conditional statements in both methods (that conditions are no longer needed), and everything should work correctly.

And, in the original source the methods were synchronized. Why did you changed this to synchronized(lock)? Just to forget to change wait to lock.wait()?

1 Comment

Yes. To protect the lock. If you don't protect the lock it is a good idea to use notifyAll(). FIXED. Thx.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.