Reputation: 2979
I was reading thread interrupting from this article. It says following:
Before a blocking code throws an
InterruptedException
, it marks the interruption status as false. Thus, when handling of theInterruptedException
is done, you should also preserve the interruption status bycallingThread.currentThread().interrupt()
.Let’s see how this information applies to the example below. In the task that is submitted to the
ExecutorService
, theprintNumbers()
method is called twice. When the task is interrupted by a calltoshutdownNow()
, the first call to the method finishes early and then the execution reaches the second call. The interruption is called by the main thread only once. The interruption is communicated to the second execution of theprintNumber()
method by the call toThread.currentThread().interrupt()
during the first execution. Hence the second execution also finishes early just after printing the first number. Not preserving the interruption status would have caused the second execution of the method to run fully for 9 seconds.public static void main(String[] args) throws InterruptedException { ExecutorService executor = Executors.newSingleThreadExecutor(); Future<?> future = executor.submit(() -> { printNumbers(); // first call printNumbers(); // second call }); Thread.sleep(3_000); executor.shutdownNow(); // will interrupt the task executor.awaitTermination(3, TimeUnit.SECONDS); } private static void printNumbers() { for (int i = 0; i < 10; i++) { System.out.print(i); try { Thread.sleep(1_000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // preserve interruption status break; } } }
I tried running above code and it prints:
01230
When I comment Thread.currentThread().interrupt();
from catch
block, it prints:
01230123456789
Though I feel I understand explanation before code, I dont think I understand why the explanation is correct, partly because I did not find any related explanation / sentence in the official doc. Here are my specific doubts:
Does that mean we need every method call in Runnable
submitted to ExecutorService.submit()
to have catch InterruptedException
and call Thread.currentThread().interrupt()
in it for whole Runnable
body to interrpt? If we miss to catch InterruptedException
and call Thread.currentThread().interrupt()
in any method call in Runnable
, then it will not be interrupted?
If above is correct, why there is not such explanation in the doc of Thread.interrupt()
?
Is it like if we want to do any closure task on interruption in any method, then only we should catch InterruptedException
, perform any task to be done and then call Thread.currentThread().interrupt()
?
If answer to question 3 is yes, then when we should be doing closure task? I feel any closure task should be done before Thread.currentThread().interrupt()
, but in the given example, break
is called after Thread.currentThread().interrupt()
.
After thinking on question 4 a bit more, I feel I dont understand it clearly how thread interruption is handled. Earlier I felt interrupted thread simply gets killed immediately, but that does not seem to be the case. How thread interruption occurs? Is there any oracle official link explaining the same?
Upvotes: 3
Views: 4567
Reputation: 5558
Thread interruption is not something that kills a thread right away. It is a suggestion for the thread to end themself.
The code in your thread will not terminate automatically. So your code should react to thread interuption by terminating gracefully. One good practice is to reset the interrupted flag everytime it was consumed, by calling Thread.currentThread().interrupt()
. Following operations that respect the status will throw InterruptedException, and will not just continue.
Thread.currentThread().interrupt();
should be the first statement in the catch block, to make sure any cleanup doesnt block again. (Using finally for cleanup is also a good idea)
In that specific case I would have pulled the try / catch out of the for loop.
It is basically a suggestion for the thread to end itself.
Java language specification 17.2.3
Interruption actions occur upon invocation of Thread.interrupt, as well as methods defined to invoke it in turn, such as ThreadGroup.interrupt.
Let t be the thread invoking u.interrupt, for some thread u, where t and u may be the same. This action causes u's interruption status to be set to true.
Additionally, if there exists some object m whose wait set contains u, then u is removed from m's wait set. This enables u to resume in a wait action, in which case this wait will, after re-locking m's monitor, throw InterruptedException.
Invocations of Thread.isInterrupted can determine a thread's interruption status. The static method Thread.interrupted may be invoked by a thread to observe and clear its own interruption status.
-
Oracle Tutorial about interrupts
The Interrupt Status Flag The interrupt mechanism is implemented using an internal flag known as the interrupt status. Invoking Thread.interrupt sets this flag. When a thread checks for an interrupt by invoking the static method Thread.interrupted, interrupt status is cleared. The non-static isInterrupted method, which is used by one thread to query the interrupt status of another, does not change the interrupt status flag.
By convention, any method that exits by throwing an InterruptedException clears interrupt status when it does so. However, it's always possible that interrupt status will immediately be set again, by another thread invoking interrupt.
Upvotes: 0
Reputation: 745
The biggest misconception with thread interruption is that this works out of the box. Yes, the basic mechanics to set the interrupt flag of a thread are made available to deal with thread interruption, but how a thread should do this is still up to the developer. See the note on ExecutorService shutdownNow
method:
There are no guarantees beyond best-effort attempts to stop processing actively executing tasks. For example, typical implementations will cancel via Thread.interrupt, so any task that fails to respond to interrupts may never terminate.
For example:
public class Hello {
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
printNumbers(); // first call
printNumbers(); // second call
});
executor.shutdownNow(); // will interrupt the task
executor.awaitTermination(3, TimeUnit.SECONDS);
}
private static void printNumbers() {
for (int i = 0; i < 10; i++) {
System.out.print(i);
}
}
}
Although the executor was shutdown and the thread was interrupted, it prints:
01234567890123456789
This is because there is no thread interruption handling whatsoever.
If you would for example test if the thread was interrupted using Thread.interrupted()
, then you can actually deal with an interruption within your thread.
To halt after the first printNumbers
you can do:
public class Hello {
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
printNumbers(); // first call
// this checks the interrupt flag of the thread, which was set to
// true by calling 'executor.shutdownNow()'
if (Thread.interrupted())
throw new RuntimeException("My thread got interrupted");
printNumbers(); // second call
});
executor.shutdownNow(); // will interrupt the task
executor.awaitTermination(3, TimeUnit.SECONDS);
}
private static void printNumbers() {
for (int i = 0; i < 10; i++) {
System.out.print(i);
}
}
}
This wil execute the first printNumbers
and print:
0123456789
The problem with your example is that whenever you catch InterruptedException
, the interrupt flag of the thread is reset to false
.
Therefore, if you would execute:
public class Hello {
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
printNumbers(); // first call
printNumbers(); // second call
});
Thread.sleep(3_000);
executor.shutdownNow(); // will interrupt the task
executor.awaitTermination(3, TimeUnit.SECONDS);
}
private static void printNumbers() {
for (int i = 0; i < 10; i++) {
System.out.print(i);
try {
Thread.sleep(1_000);
} catch (InterruptedException e) {
// the interrupt flag is reset, but do not do anything else
// except break from this loop
break;
}
}
}
}
This wil interrupt the first call to printNumbers
, but due to the catch of InterruptedException
, the thread is not considered interrupted anymore and therefore the second execution of printNumbers
will continue and the sleep
is never interrupted anymore. The output will be:
0120123456789
However, when you manually set back the interrupt flag after the catch, during the second execution of printNumbers
the sleep
is interrupted due to the interrupt flag being true and you immediately break out of the loop:
public class Hello {
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
printNumbers(); // first call
printNumbers(); // second call
});
Thread.sleep(3_000);
executor.shutdownNow(); // will interrupt the task
executor.awaitTermination(3, TimeUnit.SECONDS);
}
private static void printNumbers() {
for (int i = 0; i < 10; i++) {
System.out.print(i);
try {
Thread.sleep(1_000);
} catch (InterruptedException e) {
// the interrupt flag is reset to false, so manually set it back to true
// and then break from this loop
Thread.currentThread().interrupt();
break;
}
}
}
}
It prints 012
from the first printNumbers
execution and 0
from the second printNumbers
execution:
0120
But remember, this is only because you are manually implementing thread interruption handling, in this case due to using Thread.sleep
. As shown by this last example:
public class Hello {
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
printNumbers(); // first call
printNumbersNotInterruptible(); // second call
});
Thread.sleep(3_000);
executor.shutdownNow(); // will interrupt the task
executor.awaitTermination(3, TimeUnit.SECONDS);
}
private static void printNumbers() {
for (int i = 0; i < 10; i++) {
System.out.print(i);
try {
Thread.sleep(1_000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
private static void printNumbersNotInterruptible() {
for (int i = 0; i < 10; i++) {
System.out.print(i);
}
}
}
This will print out:
0120123456789
This is because printNumbersNotInterruptible
does not deal with thread interruption and therefore completes its full execution.
Upvotes: 5