Reputation: 697
I was playing around with following example:
public static void interrupted() throws InterruptedException {
Runnable task = () -> {
while(true && !Thread.currentThread().isInterrupted()) {
System.out.println("task executing");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
Thread thread = new Thread(task);
thread.start();
//lets allow spawned thread to run by sleeping main thread for 10 seconds
Thread.currentThread().sleep(10000);
thread.interrupt();
}
when I run this method I was expecting :
1 that the task started via Runnable would run a few times and print SOP
2 above would be possible since main thread sleeps for 10 seconds
3 once main thread wakes up it calls interrupt on the spawned thread
4 in the Runnable task we are checking for isInterrupted so this should get triggered and spawned thread should exit thereby allowing the JVM to exit.
However this is not happening and eclipse / jvm shows that the spawned thread is still running the while loop
Here is the console :
task executing
task executing
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at basics.StopEx.lambda$1(StopEx.java:39)
at java.lang.Thread.run(Thread.java:748)
task executing
task executing
task executing
task executing
........
The java docs regarding interrupt do seem to indicate that calling 'sleep' would cause a different behavior ( highlighted below ) - can someone throw some light on what is exactly happening here and why ?
Interrupts this thread.
Unless the current thread is interrupting itself, which is always permitted, the checkAccess method of this thread is invoked, which may cause a SecurityException to be thrown.
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 java.nio.channels.ClosedByInterruptException.
If this thread is blocked in a java.nio.channels.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.
Interrupting a thread that is not alive need not have any effect.
Upvotes: 2
Views: 554
Reputation: 4115
InterruptedException
and Thread.isInterrupted()
are actually quite independent. The complication was introduced when you used sleep()
in the long-running loop. Your sleep(5000)
is actually the one clearing your isInterrupted
flag, and throwing the InterruptedException
.
If you try the following, you'll see that there are actually no InterruptException
thrown when you interrupt()
the thread.
@Override public void run() {
int b = 0;
while (true) {
if (b++ == 0) System.out.println("running...");
}
}
Lesson is, you do need to check for both the exception and the flag, but they should not rely on each other. E.g. Your example catches the exception, but relies on the flag to exit your loop. As you have seen, this will not work.
You either need to break
out of the while loop when the Exception was caught, or have your try/catch
outside your loop (ensuring that catching the exception puts you out of the loop).
So, either one of the following should serve:
while (!Thread.currentThread().isInterrupted()) { // out of the loop when flag is raised
try {
//......
}
catch (InterruptedException e) {
break;//exit loop when exception thrown
}
}
// Or:
try {
while (!Thread.currentThread().isInterrupted()) { // exit loop when flag is raised
//.....
}
}
catch (InterruptedException e) {
// no need to break, as we're already outside
}
Upvotes: 2
Reputation: 103273
It's because of how the interrupt flag works for a thread.
When you interrupt a thread, basically, only one thing happens: the interrupt flag is set, and that's all that happens. It is then up to the code that this thread is running whether that actually does stuff.
The general idea is that you abort what you're doing if you can and throw some sensible exception.
A few methods built into java guarantee that this is exactly how they work. You can recognize them easily: They are all declared to throw InterruptedException
. There are many methods where behaviour when interrupting is intentionally undefined (it depends on the OS): All those blocking I/O methods act like this. If you are in a blocking network read and you interrupt the thread that's blocked on the read, that might result in an IOException on the read call, or.. not.
More importantly, when 'dealing' with an interrupt (and aborting + throwing an exception is dealing with it!), the flag is cleared.
Note that checking the flag already clears it, if you use the standard way of checking for it, which is Thread.interrupted()
. This method checks your own thread's interrupt flag and clears it. Use this one: The fact that you've written an if
statement or whatnot to deal with the interrupt means you dealt with it.
Thus, if you want to just return in response to being interrupted, the right code is:
Runnable task = () -> {
while (!Thread.interrupted()) {
System.out.println("Executing");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
return;
}
}
};
This method will just exit regardless of when the interrupt happens. In the 99.99% case itll happen whilst blocking on the sleep. Then the flag will be false, and the exception is thrown. In the unlikely case it occurs whilst processing outside of it, the while condition will pick it up.
Note that if you have the interrupt flag raised and you call a method like Thread.sleep
, that method will instantly finish execution by clearing the flag and throwing InterruptedException
. It doesn't matter where the interrupt happens, the above example will do the same thing (silently exit the run method, and the interrupt flag will be cleared).
Upvotes: 2
Reputation: 40062
Here is your code with a print statement.
public static void interrupted() throws InterruptedException {
Runnable task = () ->
{
while (true && !Thread.currentThread().isInterrupted()) {
System.out.println("task executing");
try {
Thread.sleep(5000);
}
catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
// this prints false.
System.out.println(Thread.currentThread().isInterrupted());
}
}
};
Once the interrupted state is processed, the thread is no longer considered to be in an interrupted state so the loop continues.
Upvotes: 1