It can’t have escaped anyone’s notice that I have a contrarian opinion of Java: it might not be the coolest language, but it lets me get things done. But I know it’s not perfect; in particular InterruptedException is a serious pain-point.
tldr: I’m experimenting with wrapping InterruptedException in an unchecked exception: my code is cleaner and it hasn’t yet died horribly. I think I like it.
Any time you call a long-running method, like waiting for a lock or sleeping, then that method will likely throw InterruptedException.
InterruptedException is a checked exception, which means you either need to throw it from your method or catch it.
If you catch it, you should call
Thread.currentThread().interrupt(), so that the thread-interrupted state is not lost.
(The next call to a long-running method will then throw InterruptedException again; the exception works its
way up the stack and cleanly out of the thread). Any time you catch a generic Exception, you need to handle
the InterruptedException specifically (you shouldn’t really be catching Exception, but more on that in a minute).
It’s painful to do this catch/interrupt/rethrow dance, so it’s often better just to let your method throw InterruptedException. That still makes for ugly code, because every method ends up with an InterruptedException added to the list of things it throws.
Even if you’re well disciplined and avoid catching Exception, the compounding problem is that some common interfaces
are declared to throw no checked exceptions, so you do have to catch your checked exceptions when implementing them.
(That in itself seems to be an anti-pattern; the always-excellent Guava seems to prefer to have functional
throws Exception). But a particularly nasty example is Runnable, where you always want a safety-catch,
not least because some of the thread pools really don’t cope well if you throw from a Runnable.
So you have this:
1 2 3 4 5 6 7
But… you shouldn’t do that, because then you lose the Thread.interrupted status.
So you do this:
1 2 3 4 5 6 7 8
(You could also check for InterruptedException separately, but then you need to repeat your logging / error handling).
And now we have some seriously smelly code. It’s not a factory-factory-factory yet, but we’re starting down industrialization road.
How did this happen? Why is InterruptedException checked in the first place?
Well, before InterruptedException we had ThreadDeath. When you interrupted a thread using Thread.stop, that would “immediately” throw ThreadDeath into whatever code that thread was running at the time. The problem is that it becomes almost impossible to reason about your code if anything can throw an exception. For example:
1 2 3 4 5 6 7
Now, in theory, x + y will always be 0. But, imagine that we throw ThreadDeath after incrementing x but before decrementing y: the x + y == 0 invariant is broken. This matters a great deal if it is shared state which will live on past the current thread’s death. It’s bad that this is difficult to spot as problematic code, but the killer blow is that it is basically impossible to fix.
ThreadDeath are deprecated, and really there should be no Java code left that calls
InterruptedException are not deprecated (though not exactly encouraged either). But,
we’re stuck with dealing with
InterruptedException on every call to
because it’s a checked exception.
I think Java’s language designers swung too far here though after having been burned by ThreadDeath;
exception handling in Java is inelegant enough; with InterruptedException it is just painful.
I see the argument that
throws InterruptedException is a marker that tells you to watch out for
your invariants, and that the call may be slow, but I think you should assume any method call at all might throw or be slow.
ThreadDeath was evil, it was unchecked and could happen anywhere. I know we have to restrict where exceptions can be raised, but I don’t want to deal with it being a checked exception.
Here’s one trick I’ve been trying out: wrapping every method that throws InterruptedException with a wrapper that rethrows it as a checked exception:
1 2 3 4 5 6 7 8
InterruptedError is a simple exception deriving from Error, which is unchecked and does not need to be explicitly thrown (or caught). We rely on finally blocks for cleanup, like all good code should. What’s more, we can catch Exception if we have to, and we don’t lose the thread interrupted state. We can even safely throw Exception, and not rely on our caller to remember to check whether it’s actually an InterruptedException and handle it correctly.
Now, why is it an Error and not an RuntimeException? Well, otherwise every
catch Exception would probably still have
to handle it,
and we’d be back almost where we started. Code that actually does something other than rethrow InterruptedException
is so rare that I think it should be the exceptional case. Error is supposed to be reserved for serious problems, but I think
InterruptedException falls into that camp.
How do you deal with InterruptedException? Discussion at Hacker News
Also, if anyone has code that actually does any real processing of an InterruptedException, I’d love to hear about it.