Tuesday, February 28, 2017

Is it possible to use poison pill approach with bounded queues?

Leave a Comment

In Java Concurrency In Practice book (p.156), there's a statement regarding poison pill approach:

Poison pills work reliably only with unbound queues.

Does it mean that with a bounded queue I can get a deadlock, or is it about some other liveness problems? Is it connected with number of producers and customers?

3 Answers

Answers 1

With a bounded queue, you could be prevented from adding the poison pill.

One way to avoid this issue is to make the bounded queue allow one more when a poison pill is added.

Answers 2

The problem is that the queue may be full at close time.

It depends on how valuable the data in the queue is at the time of closure. Can you afford to throw everything in the queue away?

When the time comes to close the queue it should be effective to drain the queue first before adding the poison pill.

void close () throws InterruptedException {   do {     // Empty the queue.     while ( queue.poll(0,TimeUnit.MILLISECONDS) != null ) {       // Throw it away.     }     // Keep draining the queue 'till the pill is swallowed.   } while (!queue.offer(STOP, 0, TimeUnit.MILLISECONDS)) ; } 

but of course if the items in the queue are valuable you may wish to use drainto and preserve them.

Please also bear in mind that there may be more items added to the queue after the poison pill because not only might the queue be full but there may also be threads blocked waiting to post to it.

Answers 3

@gstackoverflow: The main issue of the bounded queue is that it has a maximum capacity, so if your bounded queue is full, you will be blocked when you want to add this "Poison pill".

Bear in mind that the Poison pill has to be placed immediately and cannot wait until the queue has some space, as this technique is used to shutdown gracefully consumers when an exception occurs (otherwise there exists nicer technique to shutdown consumers).

Edit: As an example speaks more than a thousand sentences let's see a simple example (all credits for the example go to Java Concurrency in Practice), with a Producer Thread and a Consumer Thread:

public class CrawlerThread extends Thread { //The Producer Thread   public void run() {     try {       crawl(root);     } catch (InterruptedException e) { /* fall through */ }     finally {       while (true) {         try {           queue.put(POISON);           break;         } catch (InterruptedException e1) { /* retry */ }       }     }   }   private void crawl(File root) throws InterruptedException {     //some code   } } public class IndexerThread extends Thread { //The consumer Thread   public void run() {     try {       while (true) {         File file = queue.take();         if (file == POISON)         break;         else         indexFile(file);       }     } catch (InterruptedException consumed) { }   } } 

Now when you examine the Producer Thread (CrawlerThread), you see the Poison pill is placed either at the end of the run, or in a more dire situation, when an exception occurs.

Now let's say you want to use a bounded queue as the interface between the producer and the consumer, let's assume the queue is full at time t, and an exception occurs in the producer at time t, the producer won't be able to place the Poison pill on the queue and instead of having the Consumer thread shut downs, the Consumer will still be waiting for elements to come into the queue. Thus that's why the Poison pill approach is not recommended if you use a bounded queue, because it could potentially lead to an unexpected result.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment