akila
akila

Reputation: 697

Interrupt method not shutting down JVM

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

Answers (3)

NPras
NPras

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

rzwitserloot
rzwitserloot

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

WJS
WJS

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

Related Questions