Advanced   Java   Services interrupt(), isInterrupted(), interrupted() Back Next Up Home


Die Methode interrupt() [nicht statisch]

Wenn ein Threadobjekt angelegt wird, so wird ein Flag, das sogenannte Interruptflag auf false gesetzt. Wird nun für dieses Objekt die Methode interrupt() aufgerufen, so wird dieses Flag auf true gesetzt. Das folgende Beispiel zeigt dies.

Die folgende Klasse implemtiert Runnable und setzt eine Methode terminate() wie oben beschrieben ein. In einer Quasiendlosschleife in einem try catch wird der Interruptstatus ausgegeben. Mit try catch soll hier gezeigt werden, daß bei diesem Vorgang keine Exception geworfen wird.

public class Runnable1 implements Runnable
{
  private boolean running = true;

  @Override
  public void run()
  {
    try
    {
      int i = 0;
      for(;running;)
      {
        System.out.println("runnin' " + ++i);
        System.out.println("interrupted = " + Thread.currentThread().isInterrupted());
        // some work...
      }
    }
    catch(Exception ex)
    {
      System.out.println(ex);
    }
    System.out.println("end run");
  }

  public void terminate()
  {
    running = false;
  }
}

Die main()-Methode

  /**
   * Runnable1 läuft quasiunendlich
   * main wartet 10 millisec und setzt dann die interruptaufforderung ab.
   * danach wartet main wieder 10 millisec und beendet dann den thread
   * man, daß die interruptanforderung ankommt, aber keine exception auslöst
   */
  public static void main(String[] args)
  {
    Runnable1 ru = new Runnable1();
    Thread th = new Thread(ru);
    System.out.println("State = " + th.getState());
    System.out.println("isInterrupted = " + th.isInterrupted());
    th.start();
    System.out.println("State = " + th.getState());
    System.out.println("isInterrupted = " + th.isInterrupted());
    try
    {
      TimeUnit.MILLISECONDS.sleep(10);
      System.out.println("*** main interrupts " + th.getName() + " ***");
      th.interrupt();
      TimeUnit.MILLISECONDS.sleep(10);
      ru.terminate();
    }
    catch(InterruptedException ex)
    {
    }
  }

Ausschnitt aus dem Ablauf

State = NEW
isInterrupted = false
State = RUNNABLE
isInterrupted = false
runnin' 1
interrupted = false
runnin' 2
interrupted = false
runnin' 3
...
runnin' 186
interrupted = false
runnin' 187
interrupted = false
*** main interrupts Thread-0 ***
runnin' 188
interrupted = true
runnin' 189
interrupted = true
...
runnin' 335
interrupted = true
runnin' 336
end main
interrupted = true
end run

Man erkennt hier drei wichtige Eigenschaften:


Beenden mit isInterrupted() [nicht statisch]

Will man einen Thread einfach beenden, wenn eine Interruptaufforderung angekommen ist, so kann man in run() auf die Methode isInterrupted() entsprechend reagieren, etwa mit einem return.

if (Thread.currentThread().isInterrupted())
  return;

Vorsicht ist jedoch geboten, wenn man in run() sleep()-Phasen hat oder mit wait() oder join() arbeitet, siehe dazu weiter unten das besondere Verhalten dieser drei Methoden .


isInterrupted() [nichtstatisch]  versus  interrupted() [statisch]

Zur Verwirrung gibt es aber neben der nichtstatischen Methode isInterrupted() noch die statische Methode interrupted() aus der Klasse Thread. Sie arbeitet zum aktuellen Thread und damit kann man auch mit dieser Methode den Status des Interruptflags abfragen. Im nächsten Beispiel werden beide Methoden verwendet.

Unser Runnable sieht nun wie folgt aus. isInterrupted() wird einmal aufgerufen, interrupted() zweimal direkt nacheinander.

public class Runnable2 implements Runnable
{
  private boolean running = true;

  public void run()
  {
    int i = 0;
    for(; running;)
    {
      System.out.println("runnin' " + ++i);
      System.out.println("isInterrupted = " + Thread.currentThread().isInterrupted());
      System.out.println("interrupted 1 = " + Thread.interrupted() + " *****");
      System.out.println("interrupted 2 = " + Thread.interrupted() + " *****");
    }
    System.out.println("end run");
  }

  public void stop()
  {
    running = false;
  }
}

main

  /**
   * Runnable2:
   * wie Runnable1 aber ohne try catch
   * main wartet 10 millisec und setzt dann die interruptaufforderung ab.
   * main wartet wieder 10 millisec und beendet dann den thread
   */
  public static void main(String[] args)
  {
    Runnable2 ru = new Runnable2();
    Thread th = new Thread(ru);
    th.start();
    try
    {
      Thread.sleep(10);
      System.out.println("*** main interrupts " + th.getName() + " ***");
      th.interrupt();
      Thread.sleep(10);
      ru.stop();
    }
    catch(InterruptedException ex)
    {
    }
  }

Ausschnitt aus dem Ablauf. Man beachte die rot eingefärbten Zeilen!

runnin' 1
isInterrupted = false
interrupted 1 = false *****
interrupted 2 = false *****
runnin' 2
isInterrupted = false
interrupted 1 = false *****
interrupted 2 = false *****
...
...
runnin' 67
isInterrupted = false
interrupted 1 = false *****
interrupted 2 = false *****
*** main interrupts Thread-0 ***
runnin' 68
isInterrupted = true
interrupted 1 = true *****
interrupted 2 = false *****
runnin' 69
isInterrupted = false
interrupted 1 = false *****
interrupted 2 = false *****
...
...

Man sieht hier, was die statische Methode 'anrichtet':
Ist das Interruptflag auf true gesetzt so setzt ein Aufruf von interrupted() das Flag zurück auf false. Die nachfolgenden Aufrufe liefert dann false.


Zusammenfassung

Das Verhalten bei join(), sleep() und wait()

Diese drei Methoden haben das folgende besondere Verhalten:

Das folgende Beispiel zeigt, daß im catch()-Zweig das Interruptflag auf false steht.
Runnable hat nun ein sleep() und ein try catch. Der Status des Flags wird vor sleep() und in catch ausgegeben.

public class Runnable3 implements Runnable
{
  @Override
  public void run()
  {
    for(int i=0; i < 5; ++i)
    {
      System.out.println(Thread.currentThread().getName() + " runnin'" + i);

      try
      {
        boolean isInterrupted = Thread.currentThread().isInterrupted();
        System.out.println("vor sleep: isInterrupted = " + isInterrupted);
        TimeUnit.MILLISECONDS.sleep(300);
        // sleep reagiert auf die interrupt-aufforderung, setzt aber das interrupt-flag zurück
      }
      catch(InterruptedException ex)
      {
        System.out.println(ex);
        boolean isInterrupted = Thread.currentThread().isInterrupted();
        System.out.println("in catch: isInterrupted = " + isInterrupted);
        // ändert den status nicht
        System.out.println("in catch: interrupted() = " + Thread.interrupted() );
        // ändert den status auf false, falls er true war.
      }
    }
  }
}

main

private static void interrupt3()
{
  Thread th = new Thread(new Runnable3());
  th.start();

  try
  {
    Thread.sleep(300);
    System.out.println("*** + main interrupts " + th.getName() + " ***");
    th.interrupt();
  }
  catch(InterruptedException ex)
  {
  }
}

In den allermeisten Fällen sieht der Ablauf folgendermaßen auf

Thread-0 runnin'0
vor sleep: isInterrupted = false
Thread-0 runnin'1
vor sleep: isInterrupted = false
*** main interrupts Thread-0 ***
end main
java.lang.InterruptedException: sleep interrupted
in catch: isInterrupted = false
in catch: interrupted() = false
Thread-0 runnin'2
vor sleep: isInterrupted = false
Thread-0 runnin'3
vor sleep: isInterrupted = false
Thread-0 runnin'4
vor sleep: isInterrupted = false

Mit einiger Geduld kann er aber auch mal so aussehen. Hier sieht man, daß das Interruptflag tatsächlich zuerst auf true gesetzt wird, im catch()-Zweig dann aber wieder false ist.

Thread-0 runnin'0
vor sleep: isInterrupted = false
*** main interrupts Thread-0 ***
end main
Thread-0 runnin'1
vor sleep: isInterrupted = true ***
java.lang.InterruptedException: sleep interrupted
in catch: isInterrupted = false
in catch: interrupted() = false
Thread-0 runnin'2
vor sleep: isInterrupted = false
Thread-0 runnin'3
vor sleep: isInterrupted = false
Thread-0 runnin'4
vor sleep: isInterrupted = false

Beenden über den catch()-Zweig

Will man bei einer InterruptedException auf jeden Fall beenden, dann reicht ein return im catch()-Zweig.

Will man aber VOR catch durch die Abfrage von isInterrupted() reagieren so kann man sich leicht selbst in den Fuß schießen. Das folgende Runnable läuft nämlich endlos.

public class Runnable4 implements Runnable
{
  @Override
  public void run()
  {
    System.out.println("vor while");
    while(!Thread.currentThread().isInterrupted())
    {
      System.out.println("runnin'");
      try
      {
        Thread.sleep(20);
      }
      catch(InterruptedException ex)
      {
        System.out.println(ex);
      }
    }
    System.out.println("end run");
  }
}

In der obigen Situation trifft ein Interrupt praktisch immer während der sleep()-Phase ein und somit ist das Flag im catch()-Zweig bereits wieder auf false gesetzt worden und die Schleife eine Endlosschleife. Abhilfe schafft hier ein erneuter Aufruf von interrupt() in catch(). Dieser Aufruf trifft den Thread mit Sicherheit nicht in einer sleep()-Phase, das Flag wird also auf true gesetzt und bleibt auf true, da keine Exception ausgelöst wird und so wird die Schleife beendet.

public class Runnable4 implements Runnable
{
  @Override
  public void run()
  {
    System.out.println("vor while");
    while(!Thread.currentThread().isInterrupted())
    {
      System.out.println("runnin'");
      try
      {
        Thread.sleep(20);
      }
      catch(InterruptedException ex)
      {
        Thread.currentThread().interrupt();
        System.out.println(ex);
      }
    }
    System.out.println("end run");
  }
}

Dämonen

Ein Thread muß keineswegs beendet sein, wenn der Mainthread endet. Threads können länger laufen als der Mainthread. Ein Programm ist erst dann zu Ende wenn auch der letzte von main aus gestartete Thread zum Ende gekommen ist. Man kann einen Thread aber so konfigurieren, daß er bei Programmende automatisch beendet wird. Dazu erzeugt man einen sog. Dämonthread, indem man vor dem Start des Threads die Methode setDaemon(boolean on) mit true aufruft. Die JVM beendet dann diesen Thread automatisch, wenn der letzte (Nichtdämon-) Thread sich beendet hat.

Das folgende Programm startet zwei Threads, einen Dämon und einen normalen Thread. Der normale Thread läuft nach Beendigung von main noch eine Weile. Der Dämonthread enthält eine Endlosschleife, wird aber trotzdem nach dem Ende des 'Nichtdämons' beendet.

public class Daemon extends Thread
{
   public Daemon()
   {
      this.setDaemon(true);
   }

   @Override
   public void run()
   {
      for(int i = 0; i>=0 ; i++)
      {
         System.out.println("damon");
         try
         {
            sleep(500);
         }
         catch(InterruptedException ex)
         {
            System.out.println(ex);
         }
      }
      System.out.println("end damon");
   }
}
public class NoDaemon extends Thread
{
   @Override
   public void run()
   {
      for(int i = 0; i < 5; i++)
      {
         System.out.println("no damon");
         try
         {
            sleep(1000);
         }
         catch(InterruptedException ex)
         {
            ex.printStackTrace();
         }
      }
      System.out.println("end nodamon");
   }
}
public class Main
{
   public static void main(String[] args)
   {
      Daemon daemon = new Daemon();
      NoDaemon noDaemon = new NoDaemon();
      daemon.start();
      noDaemon.start();
      System.out.println("end main");
   }
}

Das Programm macht in etwa die folgende Ausgabe. Man sieht sehr deutlich, daß der Dämon erst dann beendet wird, wenn der (letzte) von main aus gestartete Thread endet (nach "NoDaemon end" erfolgt keine weitere Ausgabe).

damon
no damon
end main
damon
no damon
damon
damon
no damon
damon
damon
no damon
damon
damon
no damon
damon
damon
end nodamon
Valid XHTML 1.0 Strict top Back Next Up Home