posted 14 years ago
It looks to me like any consumer which reaches the take() step before the done flag is set will sit and wait for an object to be available and will never actually see that done flag being set. This probably comes up regularly if the consumer is much faster than the producer, or there are fewer producers than consumers.
The fixes I can think of:
1) Make Consumer threads Daemons, so any Consumers waiting in the take() method will die when the application ends. This works, but I generally like to explicitly end Threads when I can, so I avoid this one (I consider it a bit of a hack)
2) When the done flag is set, also interrupt your consumers (and producers as well) so they are forced to see the done flag. My preference, when I use this method, is to (a) not use while(true) but instead use while(!done). (b) Put the catch for the InterruptedException INSIDE the while loop. We will catch interrupted exceptions but maybe that shouldn't kill the thread.
3) You could use a poison pill approach. The problem here is caused by the fact that the Consumer is sitting in a take() statement and there is nothing to take(). So you could make the producer give consumers a sign that the queue is complete, take this poison and die. This is especially useful if you want the Consumers to finish any backlog of objects in the queue before they die (if there is a backlog, they can keep churning through the list until they reach poison). To make it work you need a fixed object that the Producer can supply and the Consumer can recognize as a signal to end. When the Producer gets the done flag, it puts the signal into the queue and then comes to an end itself. Note that you may have multiple producers and so may have multiple poison signals, that is okay. Then the Consumer churns through the queue and gets to a Poison object, recognizes it and dies. I usually make it add the poison back to the queue in case there are more Consumers than Producers to make sure all Consumers get a Poison pill. Something like this: