In one of my Java 6 applications, I have a stream that transfers the main data stream, and also preloads more records from the database. It uses an ArrayBlockingQueue queue as a FIFO buffer, and its main loop is something like that:
while (!Thread.interrupted()) { if (source.hasNext()) { try { queue.put(source.next()) } catch (InterruptedException e) { break; } } else { break; } }
There is code that does some cleanup after the loop finishes, for example, poisoning the queue and freeing up any resources, but that's almost all about it.
Accordingly, there is no direct connection with the main thread of the feeder thread: the feeder thread is configured with the appropriate parameters, and then left by itself, using the lock queue to control the data stream.
The problem occurs when the main thread must disconnect the feeder when the queue is full. Since there is no direct control channel, the shutdown method uses the Thread interface's interrupt() feed stream. Unfortunately, in most cases, the feeder thread remains locked in put() , although it is interrupted - an exception is not thrown.
From a brief reading of the interrupt() documentation and the source code for the implementation of the queue, it seems to me that quite often put() blocks without using any intermittent JVM tools. In particular, on my current JVM (OpenJDK 1.6b22), it is blocked by its own method sun.misc.Unsafe.park() . It may be using spinlock or something else, but in any case, it looks like the following case :
If none of the previous conditions has been met, this interrupt status will be set.
The status flag is set, but the thread is still blocked in put() and does not perform further iteration so that the flag can be checked. Result? A zombie thread that just wonโt die!
I understand that this is correct, or am I missing something?
What are the possible approaches to resolve this problem? Now I can only think of two solutions:
but. Call poll() several times in the queue to unlock the feeder stream: Ugly and not very reliable from what I saw, but it basically works.
b. Use the offer() method with a timeout instead of put() so that the thread can check its interrupt status for an acceptable period of time.
If I am not missing something, this is a somewhat underestimated caveat to the implementation of BlockingQueue in Java. Apparently, there are some signs of this when the documentation, for example, suggests poisoning the queues to close the workflow, but I cannot find an explicit link.
EDIT:
OK, there is a more radical variation on solution (a) above: ArrayBlockingQueue.clear() . I think this should always work, even if itโs not quite a definition of elegance ...