I've stumbled upon a situation where i have to deal with InterruptedException
, but can't pass it upwards and don't feel that my code should be allowed to swallow it. To be precise, i'm working on a distributed lock implementation, and request to backing service may be interrupted or even time out - and, of course, java.util.concurrent.Lock
doesn't account for such cases and doesn't allow me to spit out InterruptedException. I'm struggling to write correct implementation for non-throwing lock()
, tryLock()
and unlock()
methods.
So, the question is - what would be correct strategy to handle case like this? From current point of view i see only three options (and i feel smell for every of them):
lock
/ tryLock
/ unlock
methods, retrying / returning false / assuming that even if request hasn't got to it's destination, TTL will eventually unlock record. This is obviously not the best solution because it hopes that everything will be good instead of dealing with problems.RuntimeException
heir. This seems to be awful solution as well, since client code will have to work with concrete implementation rather than original interface, and unchecked exception certainly were not made for purpose like that.Thread.currentThread().interrupt()
call. I don't like this way because it basically tells thread to process it's own interrupt rather than pass a notice about call being interrupted; also, as far as i understand, if there's no outside polling, it will make thread eventually, but not instantly process interrupt, probably, in completely another place.(And, of course there's an option to allow client code configure desired behavior, but that still doesn't provide me with a really good solution)
Is there any better way than any i've described? And if no, which one should be preferred over others?
Let me discuss each one of your available options.
- Ignore interrupted exception
This is wrong. It is never right to swallow the exception when you are implementing something like a library which other users will come to rely upon. In these cases, it would never be prudent to swallow an exception unless you propagate it as a different exception which provides more meaningful information to the client. An InterruptedException
is basically a request to cancel your thread and this information should never be suppressed from the client irrespective of whether the lock would be unlocked later. The client needs to know that someone wants the unit of work being carried out by this thread to be stopped.
- Wrap in
RuntimeException
No. This is wrong as well for exactly the same reason as above. the reason for propagating an InterruptedException
is to let a client know that a request has been made to cancel an executing thread and hence wrapping it in a RuntimeException
is wrong because this information is lost.
- Use/force
Thread.currentThread().interrupt()
call
This may be right or wrong depending on the use case. Ask yourself if it would be ok for you to propagate the InterruptedException.
If it is ok to do so (it is not in your case but), then you can declare that your method throws InterruptedException
and let the callers above worry about what needs to be done. This would typically be the case when you make a call to a method (say operation()
) that throws an InterruptedException
and you won't be able to proceed further unless this call completes. Suppose operation()
throws InterruptedException
then there is nothing much you can do other than propagating this exception. So you shouldn't catch the exception. In this case just declare that your method throws InterruptedException
and you are done
If it is not ok to do so then the correct way to handle it would be to force an interrupt()
call. Using this you suppress the exception but you still give the client the option of checking the flag to see if an interruption request was made. And you are right. This requires the client side to poll rather than processing the interruption. But this is not wrong. If you don't want clients to poll then propagating the exception would have been the better option. But this is not always possible and your example is one such use case. And there are many cases where a thread of execution can return some meaningful information even when it is Interrupted. So in this cases the exception is suppressed but the information that there was a request for termination can still be passed above by calling interrupt()
method. So the client can either just use the result that was returned from a partial computation or poll to check if the interrupt flag was set depending on the use case. So you are giving the client more flexibility by doing this.
For me, the answer is almost always 3.
Java uses a cooperative interruption model: it feels like a very British approach to me:
I say, old chap, would you mind stopping what you are doing, if it is not too much trouble?
But there is no compunction to act upon the interruption in a timely way (or, indeed, at all). To use a Robin Williams quote:
Stop! ...or... I'll say stop again!
You can write your code to check for interruptions periodically, or not - it'd get very messy and repetitive if you did it everywhere. But, if you don't want to do anything when you are interrupted, you should at least preserve the fact that an interruption did occur, in order that calling code which does want to do something to act accordingly.
There is nothing really special about InterruptedException
- it is literally an empty subclass of Exception
. It is only typically only thrown in the first place if a particular method checks Thread.interrupted()
or .isInterrupted()
. So, I wouldn't worry about the fact that calling interrupt()
doesn't immediately cause the thread to stop what it is doing - that is the very nature of cooperative interruption.
To qualify why I say "almost always" above: the Java tutorial describes interruption thus:
An interrupt is an indication to a thread that it should stop what it is doing and do something else. It's up to the programmer to decide exactly how a thread responds to an interrupt, but it is very common for the thread to terminate.
I have only very, very rarely done anything other than wanting to terminate an interrupted thread.
If I wanted a thread "to do something else", I would likely be using an executor service anyway: each of the "things" to be done is represented by a separate Runnable
, so I don't even know if they are done in the same thread anyway.
So I would typically interrupt the thread, and then throw a RuntimeException
:
catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
Each Runnable
just finishes what it is doing when interrupted; the executor service decided whether or not to do another task.
It is basically only if writing framework-level code (like an ExecutorService) that I would choose to continue after interruption.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With