Java - concurrency: LinkedBlockingQueue, & ldquo; Try again if you have failed & rdquo;

advertisements
int capacity = ...
BlockingQueue q = new LinkedBlockingQueue<Element>(capacity);

Now, I do feel mildly ridiculous asking this but I'm not particularly savvy when it comes to java concurrency, so I would appreciate some help with choosing the correct way to enqueue something (and dequeue, actually, but I expect when we cleared up the one, the other will fall into place by itself).

There is, of course

while(!q.offer(e));

But I'm a bit wary of spinning implementations in a multi-threaded environment.

And I can't do

synchronized(q){
    while(!q.offer(e))q.wait();
}

either because the wakeup calls will go to internal (private) instances of Condition, meaning this would be a suicide-by-sleeping-pills implementation.

However, I'm also not particularly fond of

try{
    q.put(e);
}catch(InterruptedException ex){}

(even though it does seem to be a popular choice in online examples) because although this would do the waiting for me, I know of no reliable way to detect when an exception would force me to try again. I could do something like

boolean success = false;
do{
    try{
        q.put(e);
        success = true;
    }catch(InterruptedException ex){}
}while(!success)

But then I'd end up enqueuing the same element multiple times if the exception takes place in-between the put and the assignment to success.

I could do

boolean success = true;
do{
    try{
        q.put(e);
    }catch(InterruptedException ex){
        success = false;
    }
}while(!success)

But I remember having read (way back) that you shouldn't rely on exception handling for conditionals (though I can't seem to remember the reason why this is discouraged).

So ... what options do I have? Do I need to spin or is there something more intelligent?


It is not a good practice to catch an InterruptedException as you do since your code won't be responsive to interruption anymore. An InterruptedException is usually thrown by methods that are responsive to interruptions (current Thread being interrupted) such as the methods of type await, wait, join, sleep and many others, this should not be considered as a failure but rather as it really is, a Thread's status change that needs be taken into consideration.

As Brian Goetz explains in Java Concurrency in Practice, I quote:

When your code calls a method that throws InterruptedException, then your method is a blocking method too, and must have a plan for responding to interruption. For library code, there are basically two choices:

Propagate the InterruptedException. This is often the most sensible policy if you can get away with it just propagate the InterruptedException to your caller. This could involve not catching InterruptedException, or catching it and throwing it again after performing some brief activity-specific cleanup.

Restore the interrupt. Sometimes you cannot throw InterruptedException, for instance when your code is part of a Runnable. In these situations, you must catch InterruptedException and restore the interrupted status by calling interrupt on the current thread, so that code higher up the call stack can see that an interrupt was issued.

So in your case, you should simply use put(E) as it will make the calling thread waits for space to become available if needed and propagate the InterruptedException in order to keep on being responsive to interruptions.


But then I'd end up enqueuing the same element multiple times if the exception takes place in-between the put and the assignment to success.

This can simply never happen since an assignment of a boolean will never throw any exceptions (except a NPE in case of an un-boxing). And only methods responsive to interruption can throw such kind of exceptions as explained above which is clearly not the case of an assignment.