Reputation: 8209
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?
Upvotes: 2
Views: 666
Reputation: 73568
The put()
implementation would be the correct one, blocking until interrupted or successful. The offer()
is a bad idea if all you are doing is spinning (see first comment for disclaimer).
As Nicolas explained, the handling of the InterruptedException
is not straightforward and depends a lot on what your other code is doing, but your concern with "if the exception takes place in-between the put and the assignment to success", that can never happen: a blocking call (like put()
) can throw that exception, but it cannot occur between put()
and the assignment, or at the assignment.
Lastly, there's no need to synchronize on anything. The main idea in many of java.util.concurrent
classes is to avoid or abstract away the explicit synchronization.
Upvotes: 1
Reputation: 45005
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 catchingInterruptedException
, 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 aRunnable
. In these situations, you must catchInterruptedException
and restore the interrupted status by callinginterrupt
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 tosuccess
.
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.
Upvotes: 1
Reputation: 15714
So a few points from the LinkedBlockingQueue Javadoc:
put
method will only throw exceptions in two circumstances:
InterruptedException
s.null
, which is another bug entirely.So overall, you can just use put
to wait for space to become available. If either of these particular exceptions is thrown, then you shouldn't retry anyway.
Upvotes: 0