views:

298

answers:

3

In Brian Goetz's article on how to handle InterruptedException, one paragraph stands out:

The one time it's acceptable to swallow an interrupt is when you know the thread is about to exit. This scenario only occurs when the class calling the interruptible method is part of a Thread, not a Runnable.

I don't get this. Is the reason something to do with the Runnable possibly being handled by a threadpool whereas the Thread is something you start on your own ?

+2  A: 

I would argue that extending Thread there was unnecessary, and therefore implementing Runnable is preferred.

But the important thing is that the code knows the thread is going to exit. If your code is part of some generic callback interface then you can't know how you are being used. You could be passed to a thread pool (indeed, we probably should be using pools rather than constructing Threads at inappropriate points in the code). OTOH, usually the Runnable is an anonymous inner class and therefore, at a source level, part of the enclosing method that does know what is going on.

So, if the thread is about to exit, resetting the interrupt state on the current thread is pointless because there is nothing to interrupt.

At some point you are going to want to say that it has interrupted enough. Thread pools for instance can continue to use a thread even after a task has been interrupted, although they might want to keep the InterruptException around for the callers that attempt to pick up the task.

Libraries generally do not handle interrupts correctly. IMO, interrupts don't have the context to make sense. Life would be a lot simpler without them, unfortunately they make their presence felt.

Tom Hawtin - tackline
+4  A: 

Basically. The concern expressed in the article is that if you swallow the interupt exception then calling code higher in the stack won't know about the interuption, possibly causing undesirable behavior. If you start the thread, then you know there is nothing higher in the call stack that cares about being interupted, this thread will not continue to live in a thread pool, so just let the thread die.

I hate InterruptedException, I think it gives checked exceptions a bad name, and this article doesn't change that perspective. If it was so important that this exception pass up the call stack, Runnable.run() should have it declared in the method declaration so you can simply rethrow it, or it should have been an unchecked exception for the same reason SecurityException is an unchecked exception.

My prefered design would be that the methods return a boolean if they were interrupted if you care to know, but this article does make the case that that wouldn't necessarily be practical.

Yishai
Implementing Callable might help you return the boolean. Callable also allows you to throw exceptions upwards.
James McMahon
Returning a boolean would be a bad idea. If it doesn't clear the state, miss return value in a typical wait loop and it spins. If it does clear state, then we have interrupted the thread. The checked exception makes that clear (well perhaps, given the code I've seen, not everyone). An unchecked exception would cause an exit probably in a reasonable manner.
Tom Hawtin - tackline
@Yishai, I don't really hate InterruptedException so much. From a multithreading standpoint, it is nice to know from the method signature that is is interruptible.
ashitaka
@Tom Hawtin, given the concerns in the article, I agree that it is not necessarily a good idea to just make it a boolean. That was definitely informative for me. That being said, there are a lot of tradeoffs here, practical experience suggests that the checked exception route didn't work out, basically because rethrowing is not the appropriate response - which is surprising and therefore getting mishandled by swallowing.
Yishai
A: 

I agree with the others that the difference is whether you control that thread or not. If you extended a Thread, it's pretty much a given that you have control over that thread. On the other hand, if your code is simply a Runnable, it might be run on a borrowed thread (like from a thread pool) you do not own. By eating up the exception and not restoring the interrupt status, you deprive the code higher-up of the chance to recognize and act on the interruption.

InterruptedException being a checked exception is, I think, a good thing. An InterruptedException is a way to request a cancellation of tasks. Suppose one wrote a task in the form of a Runnable that involves a blocking method that throws an InterruptedException. If it were not a checked exception, if you're not being careful you may not think to act on the InterruptedException (thus cancellation) and do your own clean-up.

public class MyTask implements Runnable {
    public void run() {
        while (someCondition) {
            Object value = someBlockingQueue.take();
            // act on the value and loop back
        }
    }
}

Since InterruptedException is a checked exception, how my task should respond to interruption (cancellation) is front and center.

public class MyTask implements Runnable {
    public void run() {
        while (someCondition) {
            try {
                Object value = someBlockingQueue.take();
                // act on the value and loop back
            } catch (InterruptedException e) {
                // I'm being cancelled; abort
                cleanUp();
                // restore the interrupt
                Thread.currentThread().interrupt();
                break;
            }
        }
    }
}
sjlee