When should a method throw InterruptedException, and how should I handle one that does? (blocking method)

9628001 picture 9628001 · Jun 3, 2012 · Viewed 25.5k times · Source

If a method must be a blocking method, am I right in thinking that if I leave out throws InterruptedException, I have made a mistake?

In a nutshell:

  • A blocking method should include throws InterruptedException otherwise is a normal method.
  • A blocking method can compromise responsiveness because it can be hard to predict when it will complete that's why it needs throws InterruptedException.

Is that correct?

Answer

seh picture seh · Jun 3, 2012

No, I don't find your summary to be correct. Usually, if you're writing a method that calls on others that throw InterruptedException, then your method should also advertise throwing InterruptedException—unless you have a good plan for what to do when the methods on which yours relies signal interruption.

The cases where you'll be able to absorb such interruption are rare. Perhaps you're computing an iterative solution, where the precision increases with time, but, upon your calling thread being interrupted, you decide that the solution you've reached in the allotted time is good enough, and is still correct enough to return. In other words, that solution is still within your method's range.

Imagine:

private double improveUpon(double start) throws InterruptedException {
  // ...
}


public double compute() {
  double result = 0.0;
  try {
    do {
      result = improveUpon(result);
    } while (couldBeImproved(result));
  } catch (InterruptedException ex) {
    Thread.currentThread().interrupt();
  }
  return result;
}

Alternately, if you merely want to respect an interruption request, you can do so without InterruptedException being involved:

private double improveUpon(double start) {
  // ...
}


public double compute() {
  final Thread current = Thread.currentThread();
  double result = 0.0;
  do {
    result = improveUpon(result);
  } while (couldBeImproved(result) &&
           !current.isInterrupted());
  return result;
}

For yet another variation, consider the case where your method must either complete all its work or indicate to the caller that it could not complete it, and it takes a while to get there, but you want to respect thread interruption. Something like this will suffice:

private double improveUpon(double start) {
  // ...
}


public double compute() throws InterruptedException {
  final Thread current = Thread.currentThread();
  double result = 0.0;
  do {
    if (current.interrupted())
      throw new InterruptedException();
    result = improveUpon(result);
  } while (!isAdequate(result));
  return result;
}

Note there that we called on Thread#interrupted(), which has the side effect of clearing the thread's interruption status if it had been set. If that method returns true, we as the caller have accepted the responsibility to hold and communicate that interruption status. In this case, since we do not assume that we created the calling thread and we don't have enough scope visible here to know what its interruption policy is, we communicated the interruption status we observed and adopted by throwing InterruptedException.

Labeling a method as "blocking" is always a matter of degree; every method blocks its caller for some amount of time. The distinction you may be looking for is whether the method blocks waiting on some external input, such as a user pressing a key or a message arriving over a network. In those cases, advertising that you throw InterruptedException indicates to your caller that your method is safe for use by callers from threads that must control their latency. You're saying, "This may take a while to complete, but it will take no longer than you're willing to wait." You're saying, "I'll run until you tell me not to." That's different from, say, java.io.InputStream#read(), which threatens to block until one of three conditions occur, none of which is the caller's thread being interrupted.

In most cases, your decision comes down to answering the following questions:

  • To satisfy my method's requirements, do I need to call on any methods that throw InterruptedException?
  • If so, is the work I've done up to that point of any use to my caller?
  • If not, I too should throw InterruptedException.
  • If nothing I call throws InterruptedException, should I respect my calling thread`s interruption status?
  • If so, is any work I've done up to the point at which I detect that I've been interrupted of any use to my caller?
  • If not, I should throw InterruptedException.

The situations in which one will detect the current thread's interruption and swallow it are usually confined to those where you, the author, created the thread in question, and you have committed to exiting the thread's run() method once the thread gets interrupted. That's the notion of "cooperative cancellation," wherein you observe the request for your thread to stop running, and you decide to abide by that request by finishing your work as quickly as possible and letting the thread's call stack unwind. Again, though, unless you're the author of the thread's run() method, you swallowing the thread's interruption status is likely harming the intended behavior of your callers and of the other methods upon which they call.

I suggest that you study the topic of a thread's interruption status, and get comfortable with the methods Thread#isInterrupted(), Thread#interrupted(), and Thread#interrupt(). Once you understand those, and see that an InterruptedException being in flight is an alternate representation of Thread#isInterrupted() having returned true, or a courteous translation of Thread#interrupted() having returned true, this should all start making more sense.

If you need more examples to study, please say so and I can add recommendations here.