16

I have a long running task, something like:

public void myCancellableTask() {
    while ( someCondition ) {
       checkIfCancelRequested();
       doSomeWork();
    }
 }

The task can be cancelled (a cancel is requested and checkIfCancelRequested() checks the cancel flag). Generally when I write cancellable loops like this, I use a flag to indicate that a cancel has been requested. But, I know I could also use Thread.interrupt and check if the thread has been interrupted. I'm not sure which would be the preferred approach and why, thoughts?

thanks,

Jeff

Jeff Storey
  • 56,312
  • 72
  • 233
  • 406

5 Answers5

24

One problem with using interrupt is that if you do not control all code being executed, you run the risk of the interrupt not working "properly" because of someone else's broken understanding of how to handle interrupts in their library. That is the API invisibly exports an API around its handling of interrupts which you become dependent on.

In your example, suppose doSomeWork was in a 3rd-party JAR and looks like:

public void doSomeWork() {
    try { 
        api.callAndWaitAnswer() ; 
    } 
    catch (InterruptedException e) { throw new AssertionError(); }
}

Now you have to handle an AssertionError (or whatever else the library you are using might throw). I've seen experienced developers throw all sorts of nonsense on receiving interrupts! On the other hand, maybe the method looked like this:

public void doSomeWork() {
    while (true) {
        try { 
            return api.callAndWaitAnswer() ; 
        } 
        catch (InterruptedException e) { /* retry! */ }
    }
}

This "improper handling" of interrupt causes your program to loop indefinitely. Again, don't dismiss this as ridiculous; there are a lot of broken interrupt handling mechanisms out there.

At least using your own flag will be completely invisible to any 3rd-party libraries.

oxbow_lakes
  • 133,303
  • 56
  • 317
  • 449
  • 1
    Good stuff here. It's all too common to see the swallowing of the interrupt or just logging and continuing without resetting the interrupt flag. I think people get frustrated that it's a checked exception they don't know what to do with, so they don't do anything at all. – Jeff Storey Dec 16 '09 at 21:37
9

Interrupt will blast the thread out of a list of specified wait conditions. Your own cancel flag will not. If you want to interrupt waits on IO and events, use interrupt. Otherwise use your own.

Bob Cross
  • 22,116
  • 12
  • 58
  • 95
bmargulies
  • 97,814
  • 39
  • 186
  • 310
1

It depends on the doSomeWork() implementation. Is that pure computation or does it (at any point) involve blocking API (such as IO) calls? Per bmargulies's answer, many blocking APIs in JDK are interruptible and will propagate the interrupted exception up the stack.

So, if the work entails potentially blocking activities, you need'll to take interrupts into consideration even if you decide to control the process using a flag, and should appropriately catch and handle/propagate the interrupts.

Beyond that, if relying on a flag, make sure your flag is declared with volatile semantics.

alphazero
  • 27,094
  • 3
  • 30
  • 26
1

First, let's take a look at the usage conditions.

If we have a thread pool and use interruption as the cancellation mechanism, we can only interrupt the worker threads through the pool. In other words, we can't directly invoke Thread.interrupt since we don't own the threads. So, we must acquire a Future and invoke Future.cancel. Or we must call ExecutorService.shutdownNow to cancel all tasks interrupting the busy threads. In the first case, it requires some bookkeeping on our side to hold the Future handles. So the application must keep new tasks and remove the old ones.

On the other hand, if you use a global cancellation flag, we can cancel/stop multiple tasks from a central place without additional bookkeeping. But if we want to cancel an individual task - similar to invoking Future.cancel - we must store a cancellation flag for each task.

Secondly, let's examine the general convention.

Java class libraries generally interpret a thread interrupt as a cancellation request. For example, LinkedBlockingQueue.take can make our program block. But when the current thread is interrupted it throws an InterruptedException and returns. So our application becomes responsive by using a responsive method. So we can just build upon the already existing support and write additional Thread.interrupted/Thread.currentThread().isInterrupted checks in our code.

Moreover, the cancellation methods in ExecutorService use thread interruption. As we mentioned, Future.cancel and ExecutorService.shutdownNow rely on interrupts.

isaolmez
  • 1,015
  • 11
  • 14
0

I think that it's a matter of preference in most cases. Personally I would go for the hand-made flag. It gives you more control - for example, this way you make sure that your thread doesn't leave some other object in an inconsistent state. Besides, if performance is really critical, bear in mind that using exceptions has an overhead (even if it's negligible in 99% of the cases).

Eli Acherkan
  • 6,401
  • 2
  • 27
  • 34