The other Thread
did not join()
your Thread
in case of an InterruptedException
. join()
is supposed to wait forever or until the calling Thread
is interrupt()
-ed.
First you need to consider whether or not an InterruptedException
can actually happen. There are several possibilities how to deal with it. You can:
- Swallow it. Warning Only do so if you're sure that
InterruptedException
will never happen. (Are you sure? Really?)
- Set the interrupted flag again. This is what you do if your code doesn't know how to handle it but the Thread is doing other stuff as well that might be interested in the
InterruptedException
.
- Propagate it. This is the best solution if your current method doesn't know what to do with it, but maybe the caller knows.
- Handle it. Maybe your current
Thread
simply wants to stop (by completing its run()
method) when you received the InterruptedException
.
- Wrap it in another exception. There are two variants of this.
- You may want to wrap it in an Assertion if the
InterruptedException
shouldn't have happened in the first place and clearly is an error.
- You may want to wrap it in another exception if your method is bound to a specific contract that doesn't allow
InterruptedException
.
In any case, I strongly recommend to log()
the InterruptedException
in some way. If you don't log it, and it happens, the program's behavior might be difficult to understand without that log information about the InterruptedException
.
Swallowing it
Warning Only swallow it if you are absolutely sure that this is okay in your case. You have been warned.
public static void joinThread(final Thread thread) {
while (true)
try {
thread.join();
return;
} catch (final InterruptedException e) {
Logger.getGlobal().log(Level.ERROR, "Unexpected InterruptedException", e);
}
}
As a variant of this, you can simply ignore it without a retry.
public static void joinThread(final Thread thread) {
try {
thread.join();
} catch (final InterruptedException e) {
Logger.getGlobal().log(Level.ERROR, "Unexpected InterruptedException", e);
}
}
Setting the interrupted
flag again.
This is what you do if the Thread
is doing other stuff as well, your code doesn't know how to deal with the InterruptedException
but the other code does.
public static void joinThread(final Thread thread) {
try {
thread.join();
} catch (final InterruptedException e) {
Logger.getGlobal().log(Level.INFO, "Unexpected InterruptedException, resetting flag", e);
Thread.currentThread().interrupt();
}
}
But be warned! This only is a good idea if other code really knows what to do. If the other code does the same thing, and the stuff is in a loop (which is often the case for Thread
s), you've just turned your nice blocking code into busy-waiting garbage because the interrupted
flag never gets cleared properly. While this solution is advertised as the best or true solution in many blog articles, it's nonsense as long as there isn't any code that knows how to deal with InterruptedException
and actually keeps the flag cleared instead of resetting it.
Propagating it
This is the best solution in case the method itself doesn't know how to handle it, but the caller might know.
public static void joinThread(final Thread thread) throws InterruptedException {
try {
thread.join();
} catch (final InterruptedException e) {
Logger.getGlobal().log(Level.FINE, "Got InterruptedException, propagating it to the caller", e);
throw e;
}
}
In case the caller does something nice with it, you can remove the unnecessary parts like logging and re-throwing.
Handling it.
In case the method itself knows that when interrupted, it has to do something, it can simply catch the exception and do whatever is expected.
public static void joinThread(final Thread thread) {
try {
thread.join();
} catch (final InterruptedException e) {
Logger.getGlobal().log(Level.FINE, "Got InterruptedException, handling it", e);
// ...whatever...
}
}
Wrapping it.
If the exception was not supposed to happen, because the application does not support interruption, and Thread.interrupt()
or ThreadGroup.interrupt()
shouldn't have been called in the first place, turning the exception into an AssertionError
is the way to go.
There are two possibilities to do so.
// Only throws AssertionError if assertions are enabled.
public static void joinThread(final Thread thread) {
try {
thread.join();
} catch (final InterruptedException e) {
Logger.getGlobal().log(Level.ERROR, "Unexpected InterruptedException", e);
assert false : e;
}
}
// Always throws AssertionError.
public static void joinThread(final Thread thread) {
try {
thread.join();
} catch (final InterruptedException e) {
Logger.getGlobal().log(Level.FATAL, "Unexpected InterruptedException", e);
throw new AssertionError(e, "Unexpected Thread interruption.");
}
}
If the InterruptedException
is to be propagated but the caller doesn't support the exception and cannot be changed, you can wrap it into an exception that is supported. Whether or not this works and is a good idea really depends much on the caller.
public static void joinThread(final Thread thread) {
try {
thread.join();
} catch (final InterruptedException e) {
Logger.getGlobal().log(Level.ERROR, "Unexpected InterruptedException", e);
throw new SomeException(e, "Unexpected Thread interruption.");
}
}
Final notes
There has been some discussion that swallowing is always bad. That's not true. If you have 100 lines of code and do some Thread.sleep()
somewhere for some good reason, swallowing is usually fine. As always, it depends. In the end, the semantics of Thread.interrupt()
are not so different from SIGHUP
, and SIGHUP
is usually ignored.
There also has been some discussion whether turning it into an AssertionError
is a good idea. AssertionError
is an unchecked Throwable
, not even an Exception
, which means the compiler doesn't force code to deal with it and most code isn't written to deal with it - that's intentional, because by using AssertionError
we say here's an error that was so unexpected that the code doesn't know how to deal with it and therefore wants to stop. Whether AssertionError
terminates the VM depends on which Thread(s) would be taken down due to the AssertionError
being thrown up to the UncaughtExceptionHandler
s of the affected Thread
s.