S Kr
S Kr

Reputation: 1840

Interrupting a unresponsive thread

From the book "java concurrency in practice" , there is a statement - > if the task is not responsive to interruption, timedRun will not return until the task finishes, which may be long after the desired timeout

private static final ScheduledExecutorService cancelExec = ...;

public static void timedRun(Runnable r,
                           long timeout, TimeUnit unit) {
    final Thread taskThread = Thread.currentThread();
    cancelExec.schedule(new Runnable() {
        public void run() { taskThread.interrupt(); }
    }, timeout, unit);
    r.run();
}

Does that means that the interrupt() function might get stuck? What may cause the interrupt() to stuck. It is just setting the isInterrupted flag of the targeted thread. I foresee unless and until any process might starve the process that is calling the interrupt(), i don't think the interrupt function will get stuck.

Upvotes: 0

Views: 174

Answers (4)

Pramod Kishore
Pramod Kishore

Reputation: 315

The whole point of confusion here is that the author introduced the argument Runnable r in the parameter of the method timedRun.

Just I am trying to simplify it by removing that parameter and introducing a new task.

Let's consider our TimedRun class is recreated as below

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TimedRun {
public static final ScheduledExecutorService cancelExec = new   ScheduledThreadPoolExecutor(
        1);

public static void timedRun(long timeout, TimeUnit unit) {

    final Thread taskThread = Thread.currentThread();
    System.out.println(taskThread);
    cancelExec.schedule(new Runnable() {
        public void run() {
            taskThread.interrupt();
        }
    }, timeout, unit);

    runAnytask();
}

private static void runAnytask() {

    System.out.println(Thread.currentThread());
        while (true) {
            // do some work here infinitely
}

}

And let's create a caller of the method timedRun as below

import java.util.concurrent.TimeUnit;

public class TimedRunCaller {
public static void main(String[] args) {
TimedRun.timedRun(1L, TimeUnit.SECONDS);
System.out.println("Now it should terminate");
TimedRun.cancelExec.shutdown();
}

}

Here the author want to say , if the task , that is in our case the method runAnytask , is not responsive to interruption, then timedRun method which is using this runAnytask as task will not return to the caller utill the task(runAnytask) finishes.But notice here the runAnytask method is looping infinitely.So it will never finish.

Although the timedRun method is interrupting the caller thread (here the caller of the timedRun method is the main thread) ,but runAnytask method has no mechanism to response the interrupt.So the timedRun method will never returned to caller.

But if we modify our runAnytask as below manner

private static void runAnytask() {

    System.out.println(Thread.currentThread());
    long start = System.currentTimeMillis();
    try {
        while (true) {
            if (Thread.currentThread().isInterrupted()) {
                System.out.println("interupted");
                break;
            }
        }
    } catch (Exception e) {
        System.out.println("interuptrd "
                + (System.currentTimeMillis() - start));
    }

} 

We can see that now the task is responsive to the interruption and it will response to the interruption raised by the timedRun method.And it will return to the caller after the specified timeout.

So author says you should know the rules: you should know a thread's interruption policy before interrupting it.Whether it is designed to respond the interruption or not.Otherwise your interruption you go unnoticed as in our first case of runAnytask method.

I hope it clears everything now.

Upvotes: 0

Serdar
Serdar

Reputation: 383

Another solution can be like, another thread executes long running task and the caller thread (the thread where "timedRun" is called) waits for the task completion as follows:

/**
     * Runs a long running task with a timer.
     * 
     * @param longRunningTask long running task
     * @param timeout as milliseconds, method will return if long running task is not     completed by this time
     * @throws InterruptedException
     */
    void timedRun(Runnable longRunningTask, long timeout)
            throws InterruptedException {

        // Thread that executes runnable
        Thread newThread = new Thread(longRunningTask);
        newThread.start();

        // Current thread joins the new thread with a timeout
        Thread.currentThread().join(timeout);

        // Time expired, longRunningTask might be completed or not
        newThread.interrupt();  //optional

    }

Upvotes: 0

Radiodef
Radiodef

Reputation: 37845

No, it means that if r does not check for the interruption during run somehow, interrupting it will not do anything.

The structure of this method is pretty convoluted.

public static void timedRun(Runnable r,
                       long timeout, TimeUnit unit) {

    // reference to the thread calling the method
    final Thread taskThread = Thread.currentThread();

    cancelExec.schedule(new Runnable() {
        public void run() {               // another thread is
            taskThread.interrupt();       // scheduled to interrupt
        }                                 // the calling thread later
    }, timeout, unit);

    r.run(); // run a long running task on the calling thread
             // this is where interrupt may or may not be seen later
             // this is the "task" the blurb is referring to
}

If I call this method like the following:

timedRun(new Runnable() {
    @Override
    public void run() {
        long start = System.currentTimeMillis();
        try {
            Thread.sleep(2000L);
        } catch(InterruptedException e) {}
        System.out.println(System.currentTimeMillis() - start);
    }
}, 1L, TimeUnit.SECONDS);

It will output something around 1000 because interrupting causes the exception to be thrown during sleep.

If I do this:

timedRun(new Runnable() {
    @Override
    public void run() {
        while(!Thread.interrupted()) {
            /* do whatever */
        }
    }
}, 1L, TimeUnit.SECONDS);

It will also see the interruption after ~1 second because I am checking for it.

If I do this:

timedRun(new Runnable() {
    @Override
    public void run() {
        while(true);
    }
}, /* doesn't matter */, /* doesn't matter */);

It will never return. Potentially the program will just freeze.

Upvotes: 2

Chris Hayes
Chris Hayes

Reputation: 12040

The key is that timedRun cannot return until r.run() returns. If the Runnable r ignores the fact that it has been interrupted, then r.run() might return very late indeed. It is not the interrupt call being stuck (it will likely complete almost immediately), but rather that the interrupt can be ignored by its target, preventing timedRun from completing until the run method reaches its natural end, if any.

Upvotes: 1

Related Questions