Search This Blog

Tuesday 22 October 2013

Interrupting a Thread

I was asked to do a revision in threads a while back - so as to then start understanding the concurrency package. I decided to do it from the Sun's Concurrency tutorial. I liked the part of sleeping and interrupting threads. Consider the simple program below:

public class PrintInfo {
  static String stationMessages[] = { 
      "Train to Bangalore leaves at 16.40 hrs.", 
      "Do not Loiter",
      "Clean Station .... Safe Station", 
      "Use the Footbridge to cross over" 
  };

  public static void display() throws InterruptedException {
    for (int i = 0;; i++) {
      if (i == stationMessages.length) {
        i = 0;
      }
      // Pause for 1 seconds
      Thread.sleep(1000); // static method - can only sleep the current thread
      // Print a message
      System.out.print(stationMessages[i] + "\r");
    }
  }

  public static void main(String[] args) throws InterruptedException, IOException {
    display();
  }
}
The class does nothing complex. It simply prints a continuous list of info messages to the console. Here we have used the sleep method to sleep the thread for minimum 1 second after it displays a message.We know that the sleep method can throw an InterruptedException.
What does it mean ? Why interrupt ? An interrupt is a mechanism to tell the thread "Stop your current activity and respond to this request !". Lets modify the above code for interrupts.
Consider that we have a low priority thread displaying the info messages on the console. Another Thread however would like to interrupt this thread and take over the console. Consider the below code:
public class PrintInfo {

  static InfoThread infoThread;

  static class InfoThread extends Thread {

    InfoThread() {
      super.start();
    }

    @Override
    public void run() {
      display();
    }
  }

  static class EmergencyDisplay implements Runnable {

    private Thread infoThread;
    private String message;

    public EmergencyDisplay(Thread infoThread, String msg) {
      this.infoThread = infoThread;
      this.message = msg;
    }

    @Override
    public void run() {
      if (infoThread.isAlive()) {
        infoThread.interrupt();
      }
      System.out.println("**********Important Message***********");
      System.out.println(this.message);
      System.out.println("**************************************");

      infoThread = new Thread(new Runnable() {

        @Override
        public void run() {
          display();
        }
      });
      infoThread.start();
    }
  }

  static String stationMessages[] = { 
    "Train to Bangalore leaves at 16.40 hrs.", 
    "Do not Loiter",
    "Clean Station .... Safe Station", 
    "Use the Footbridge to cross over" };

  public static void display() {
    for (int i = 0;; i++) {
      if (Thread.interrupted()) {
        break;
      }

      if (i == stationMessages.length) {
        i = 0;
      }
      // Pause for 1 seconds
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        break;
      }
      // Print a message
      System.out.print(stationMessages[i] + "\r");
    }
  }

  public static void main(String[] args) throws InterruptedException {
    infoThread = new InfoThread();

    infoThread.start();
    Thread.sleep(1000);
    new Thread(new EmergencyDisplay(infoThread, "Deccan Express Will arrive in 10 minutes on Platform No 3")).start();
  }
}
Here we have a new Thread - infoThread class that displays the list of messages. Whenever there is an emergency message to be displayed, we need to stop our InfoThread and then display the message. For the stop operation we have used an interrupt. Consider the run method of our EmergencyThread:
public void run() {
      if (infoThread.isAlive()) {
        infoThread.interrupt();
      }
The Thread checks if the info thread is alive and running in which case it sends an interrupt to it. But what happens to the Thread when it is interrupted ? From the Sun tutorial:
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.
As stated the status flag is set for a thread. Hence the code:
if (Thread.interrupted()) {
   break;
}
The thread could also be asleep when the interrupt occurs. The sleep method always handles the interrupt by throwing an InterruptedException. This is as per the above stated convention. Accordingly we have handled the case in the catch block of sleep.
I found a very good link on dealing with InterruptedException. I picked some tips from the same and decided to add it here (copied word for word) :
  1. Interruption is a cooperative mechanism. When one thread interrupts another, the interrupted thread does not necessarily stop what it is doing immediately. Instead, interruption is a way of politely asking another thread to stop what it is doing if it wants to, at its convenience. Some methods, like Thread.sleep(), take this request seriously, but methods are not required to pay attention to interruption 
  2. If throwing InterruptedException means that a method is a blocking method, then calling a blocking method means that your method is a blocking method too, and you should have a strategy for dealing with InterruptedException. Often the easiest strategy is to throw InterruptedException yourself.
  3. Sometimes it is necessary to do some amount of cleanup before propagating the exception. In this case, you can catch InterruptedException, perform the cleanup, and then rethrow the exception.
  4. Sometimes throwing InterruptedException is not an option, such as when a task defined by Runnable calls an interruptible method. In this case, you can't rethrow InterruptedException, but you also do not want to do nothing. When a blocking method detects interruption and throws InterruptedException, it clears the interrupted status. If you catch InterruptedException but cannot rethrow it, you should preserve evidence that the interruption occurred so that code higher up on the call stack can learn of the interruption and respond to it if it wants to. This task is accomplished by calling interrupt() to "reinterrupt" the current thread.
    catch (InterruptedException e) {
      // Restore the interrupted status
      Thread.currentThread().interrupt();
    }
    
  5. If you cannot rethrow InterruptedException, whether or not you plan to act on the interrupt request, you still want to reinterrupt the current thread because a single interruption request may have multiple "recipients." The standard thread pool (ThreadPoolExecutor) worker thread implementation is responsive to interruption, so interrupting a task running in a thread pool may have the effect of both canceling the task and notifying the execution thread that the thread pool is shutting down. If the task were to swallow the interrupt request, the worker thread might not learn that an interrupt was requested, which could delay the application or service shutdown.

No comments:

Post a Comment