3

I'm reading Goetz's Java Concurrency In Practice where this example code is shown:

public final class Indexer implements Runnable {

    private final BlockingQueue<File> queue;

    public Indexer(BlockingQueue<File> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        try {
            while (true) {
                queue.take();
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

}

with the description:

Restore the interrupt. Sometimes you cannot throw InterruptedException, for instance when your code is part of a Runnable . In these situations, you must catch InterruptedException and restore the interrupted status by calling interrupt on the current thread, so that code higher up the call stack can see that an interrupt was issued, as demonstrated in Listing 5.10 .

In the example code, "code higher up the call stack" would never see an interrupt if this code executed - or am I making the wrong deduction? The thread here just dies after calling interrupt(), correct?

So the only way this interrupt() could be useful is if it in within a loop, correct?

Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
Adam
  • 5,215
  • 5
  • 51
  • 90

4 Answers4

2

The thread here just dies after calling interrupt(), correct?

The executing thread will not finish after an interrupt, you're thinking Thread#stop. Thread thread may continue to run even after the runnable completes. The Runnable is simply a task that a thread runs.

After that task completes, is it important for the thread to know interruption occurred? What if the Thread needs to respond to some other cancellation request and that is being done by another thread?

Because you are simply a Runnable task you don't have those answers and as such you should let the Thread know an interruption did occur so that the thread can handle it the way it wants to.

John Vint
  • 39,695
  • 7
  • 78
  • 108
  • I'm not 100% certain what you're trying to say. The OP clearly is assuming that any instance of the given `Indexer` class will be the delegate of a `Thread`, and in that case, there will be no "code higher up the call stack." It seems like you are saying, "Don't assume that! You don't know who's calling that `run()` method." Am I right? – Solomon Slow Jul 21 '16 at 18:50
  • That is correct. It could be the case that the `Indexer`'s relationship to a Thread is 1-1. The point I am making (similar to Goetz) is that it may not be the case and if it may not be the case one should always propagate the interruption. – John Vint Jul 21 '16 at 19:29
  • It's slightly abrasive to say I was thinking `Thread#stop()`. The `Runnable` just runs out of code to run after the `try-catch` block. Could you give a code example to demonstrate what you mean by `Thread` responding to some other cancellation request? Or at least expand on the idea? – Adam Jul 22 '16 at 09:44
1

In Java you need to handle Thread interruption. To do that polling Thread.isInterrupted() is necessary. When your code is part of a Runnable you need to set the interrupted flag of the Thread in case of an InterruptedException so that succeeding tasks run by this Thread (code higher up the call stack) can poll Thread.isInterrupted() and handle interruption appropriately. This also gives the thread a chance to exit cleanly instead of killing it right away. This is what Thread.stop() does.

Object.wait and Thread.sleep are two examples that first check the flag and throw an InterruptedException if it's set.

Selim Ekizoglu
  • 519
  • 5
  • 6
1

I've been conflicted about this when writing toy examples, I hate to put in something like Thread.currentThread().interrupt() when in the example it doesn't do anything, but I also want my example to be demonstrating good practice, which means setting the interrupt flag.

If you pass the Runnable into a Thread then obviously there's nothing that's going to read the interrupt restored as the last thing done by the Runnable. When the thread terminates the interrupt flag value is no longer available.

If the Runnable is passed into an Executor then the Executor is in charge of setting the interrupted flag on its threads. Also the Executor should know better than to rely on the tasks to restore the interrupt flag. So it usually shouldn't matter if an Executor task doesn't restore the interrupt status.

But there may be edge cases; a Runnable may need to be run by the current thread as opposed to getting handed off to another thread. For example, if you're submitting the task to an Executor which has a rejection policy that makes the task run in the calling thread, then interruptions to the calling thread could be lost if it's running a task rejected from the executor that doesn't restore the interrupt flag.

It would be better to be able to change the configuration for the executor without having to worry about whether the tasks are well-behaved for that configuration. Tasks are leaky abstractions and this isn't possible in general, but for this specific issue it is: have your tasks restore the interrupt flag in all cases, just to be safe.

Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
0

The Java documentation specifies the effect of Thread.interrupt() as

If this thread is blocked in an invocation of the wait(), wait(long), or wait(long, int) methods of the Object class, or of the join(), join(long), join(long, int), sleep(long), or sleep(long, int), methods of this class, then its interrupt status will be cleared and it will receive an InterruptedException.

If this thread is blocked in an I/O operation upon an InterruptibleChannel then the channel will be closed, the thread's interrupt status will be set, and the thread will receive a ClosedByInterruptException.

If this thread is blocked in a Selector then the thread's interrupt status will be set and it will return immediately from the selection operation, possibly with a non-zero value, just as if the selector's wakeup method were invoked.

If none of the previous conditions hold then this thread's interrupt status will be set.

Notice the last paragraph; essentially, unless your thread is currently performing one of the mentioned wait-style operations, interrupting the thread simply sets a flag, but doesn't otherwise affect the flow of control. This is true regardless of whether the call comes from a different thread or the victim thread itself.

In other words:

try {
   while (true) {
        queue.take();
    }
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
}

// When an interrupt is received, the thread resumes
// execution here. It will notice the effect of the
// Thread.currentThread().interrupt() only, if it tries
// to perform any of the mentioned special wait operations
// here.

synchronized (someLockObject) {

    while (!someCondition) {

        // Will immediately terminate with an `InterruptedException`,
        // since we properly restored the interrupt status
        // above.

        someLockObject.wait();
    }
}

This method achieves, that all code in the current call-stack has a chance to recognize the interruption, and perform the necessary clean-up and an "orderly" shut down. If you do not restore the interrupt state, then code following the try/catch may wrongly assume, that everything is fine, and that it should resume execution normally.

Dirk
  • 30,623
  • 8
  • 82
  • 102
  • so you're basically saying Mr Goetz could have fleshed out his example a little bit so that my question would never arise? – Adam Jul 22 '16 at 09:46
  • This is one way to put it. Another could be: "Don't stop at the book. Also read the documentation, and find out, what Thread.interrupt() actually does..." – Dirk Jul 22 '16 at 17:03
  • please don't just say RTFM. Unfortunately the javadoc is not so comprehensive. As everyone who has read the current SO headline banner on documentation will recognise, examples are key here and this issue is not directly tackled let alone illustrated by example by either the `#interrupt()` or the class level javadoc. Your example puts in extra code after the call to `interrupt` - are there any other situations where the call to `interrupt` can be the last code executed in the `Runnable` yet the `Thread` or an associated `Future` or anything else reads that interrupt status? – Adam Jul 23 '16 at 15:46
  • I think, you misunderstand the scope of the example given in the book. The example is there to tell you *what* you should do, not *why* you should do it. It is quite sufficient to show, what it is supposed to show. Now, your question goes to the *why*, and in this context, the snippet might *not* be sufficient, but the question is a different one. Yes, examples are important and can be helpful, when you want to understand a topic. But so is reading the documentation. – Dirk Jul 24 '16 at 15:51