Thread.interrupt()

The only sensible way to cancel an executing Java thread is by interrupting it. This will set a boolean interrupted status flag in the thread object to true. Calling interrupt on a thread does not mean that it will immediately stop. It just requests the thread to interrupt itself at the next blocking call. These blocking calls comprise methods such as sleep, wait and join. They are known as cancellation points. If the interrupted status is set, these methods clear that flag and throw an InterruptedException. What should a caller do if it catches an InterruptedException? The answer depends on the ownership of the thread. The object that launched the thread is considered its owner. For instance, a thread pool is the owner of all worker threads that it started. Only the owner knows what to do when a thread is interrupted. Code executed by the owner when a thread is interrupted is known as its interruption policy or cancellation policy. The owner will be denied the chance to execute this policy if the InterruptedException is not propagated up to it. To make this idea clear, let us look at the implementation of a simple TaskExecutor. This class receives a Task and executes it in a thread. TaskExecutor also exposes a method to cancel a running task. A task is represented by this simple interface:

public interface Task {
    public void execute () throws InterruptedException;
}

execute throws the InterruptedException so that implementers get a chance to propagate it to the TaskExecutor.

The TaskExecutor receives a task through its submit method. This task is passed on to a TaskThread where it is executed. The TaskExecutor object owns this thread. When it is interrupted, the TaskThread calls the appropriate method that executes the interruption policy as defined by TaskExecutor. An interruption policy may free up some thread specific resource, reset some state or do something else that only the owner knows of. (As we have only one thread in the TaskExecutor, the thread also calls a global cleanup method before it finish running. In the case of a thread pool, this method might be called before the last thread is interrupted or when it exists normally). Here is the code that defines the TaskThread class:

public class TaskThread extends Thread {

     public TaskThread (Task task, TaskExecutor owner) {
         this.task = task;
         this.owner = owner;
     }

     public void run () {
         try {
             task.execute ();
         } catch (InterruptedException ex) {
             if (owner != null)
                 owner.executeInterruptionPolicy ();
         } finally {
             if (owner != null) {
                 owner.cleanup ();
             }
         }
     }

     private Task task;
     private TaskExecutor owner;
 }

Now, the definition of TaskExecutor itself:

public class TaskExecutor {

    public void submit (Task task) {
        t = new TaskThread (task, this);
        t.start ();
        running = true;
    }

    public void executeInterruptionPolicy () {
        System.out.println ("executing interruption policy ...");
    }

    public void cleanup () {
        if (running) {
            System.out.println ("cleaning up ...");
            running = false;
        }
    }

    public void cancel () {
        if (running)
            t.interrupt ();
    }

    public boolean isRunning () {
        return running;
    }

    private TaskThread t;
    private boolean running;
}

The cancel method just calls interrupt on the running thread, so that, if the task makes a blocking call on the thread, it will throw an InterruptedException. As the Task implementation do not own the thread and cannot be sure of its interruption policy, it should not consume this exception. Here is a Task implementation that correctly hands over the InterruptedException to someone who knows what it means to interrupt this particular thread:

public class CounterTask implements Task {

    public void execute () throws InterruptedException {
        int i = 0;
        while (i < 10) {
            System.out.print (++i + " ");
            try {
                Thread.sleep (500);
            } catch (InterruptedException ex) {
                throw ex;
            }
        }
    }
}

The following code submits a CounterTask to the executor. User can cancel the task by typing the ‘q’ key:

public class Test {
    public static void main (String args[]) {
        TaskExecutor t = new TaskExecutor ();
        t.submit (new CounterTask ());
        while (t.isRunning ()) {
        try {
            int c = System.in.read ();
            if ((char)c == 'q') {
                t.cancel ();
                break;
            }
        } catch (java.io.IOException ex) { }
    }
}

A sample run:

$ java.exe Test
1 2 3 4 5 6 q
executing interruption policy ...
cleaning up ...

If you comment out throw ex; in CounterTask.execute(), TaskExecutor.executeInterruptionPolicy() will not run.

To summarize :– The proper way to cancel a running thread is to call interrupt on it. Its interrupted status can either be polled (by calling isInterrupted) or by catching the InterruptedException. Consume this exception only if you own the thread. Otherwise re-throw the exception and give the owner of the thread a chance to execute the proper interruption policy.