24.8 Beenden von Threads
 
  Um einen Thread zu beenden, liegt es nahe, die stop()-Methode der Klasse Thread zu benutzen. Tatsächlich führt ihr Aufruf zu einem sofortigen Abbruch des Threads, wobei alle Sperren aufgehoben werden. Nun ist die stop()-Methode deprecated (missbilligt). Die Fa. SunTM rät also dazu, diese Methode nicht mehr zu benutzen. Was ist der Grund dafür? Nun, wenn ein Thread, der gerade eine Sperre auf ein Objekt hält, komplexere und u. U. langwierige Operationen ausführen. Wird dieser Thread unvermittelt gestoppt, so lässt er das Objekt, auf das er eine Sperre hatte, unter Umständen in einem nicht definierten Zustand. Das ist so, als wollten wir ein Auto, das sich mitten in der Fahrt befindet, ohne gezieltes Anhalten, einfach ausschalten. Wir zeigen dies an einem Beispiel.
 
Download:
TestThread1. java
public class TestThread1 implements Runnable{

  Thread thread = null;
  
  public synchronized void start(){
    if (thread == null){
      thread = new Thread(this);
      thread.start();
    }
  }
  
  public synchronized void stop(){
      thread.stop();
  }
  
  public void run(){
    long startZeit = System.currentTimeMillis();
    int i = 1;
    System.out.println("Thread gestartet");
    while (thread != null){
      try{
        Thread.sleep(1000);
        System.out.println(i++);
      }
      catch(InterruptedException e){
        break;
      }
    }
    long dauer = System.currentTimeMillis()-startZeit;
    System.out.println("Thread gestoppt, Lebensdauer: "
                       + dauer);
  }
  
}
Bemerkungen Die Klasse TestThread ist der Klasse Thread nachempfunden: sie emplentiert Runnable, besitzt den nicht extra implementierten Standardkonstruktor und die Methoden start(), stop() und run().
 
start() Was passiert noch? Unsere Klasse aggregiert ein Thread-Objekt. Warum tun wir das? Die in unserer Klasse implementierte Methode start()soll die Methode start() der Klasse Thread aufrufen. Das geht aber nur dann, wenn das Objekt der Klasse TestThread1 von einem Thread-Objekt kontrolliert wird. Deshalb wird in unserer Klasse ein Objekt thread der Klasse Thread deklariert und zunächst auf null gesetzt. Es existiert also noch keine Referenz auf dieses Objekt. Mit thread = new Thread(this); in unserer Methode start() erfolgt dann die Instanziierung und die Übergabe der Instanz unserer Klasse TestThread (deshalb this) an thread. Dann können wir den Thread mit der Thread-Methode start() starten.
stop() Diese Methode ist sehr einfach, sie startet lediglich die Thread-Methode stop(), tut also nichts anderes als die deprecated-Methode.
run() Solange der Thread unser Objekt kontrolliert, wird in der run()-Methode eine Schleife ausgeführt: Ausgehend von der 1 wird also hoch gezählt und die Werte ausgegeben. Damit der Vorgang lange genug geht, wir simulieren einen langwierige Berechnung, lassen wir den Thread nach jeder Inkrementierung eine Sekunde 'schlafen'. Das Inkrementieren kann also nur beendet werden, wenn man den Thread beendet.
 
TestKlasse

Download:
BeendenThread1. java

public class BeendenThread1 {
  static TestThread1 testThread = new TestThread1();
  
  public static void main (String[] args) 
                     throws InterruptedException {
    testThread.start();
    Thread.sleep(5000);
    testThread.stop();
  }
}
Bemerkungen Unsere Testklasse erzeugt eine Instanz unseres TestThreads, startet diese, wartet 5 Sekunden und stoppt ihn.
 
Ausgabe Thread gestartet
1
2
3
4

C:\Java
Drücken Sie eine beliebige Taste . .
.
 
  Im ersten Moment wundert, dass nur bis 4 hoch gezählt wurde. Tatsächlich war die Schleife gerade dabei, weiter hoch zuzählen, also sie 'gewaltsam' daran gehindert wurde. Es wurde also nicht die Schleife beendet sondern die ganze run()-Methode abgebrochen.  Deshalb fehlt auch die Endausgabe:
Thread gestoppt, Lebensdauer: ....
 
  Wir wollen nun zeigen, wie ein kontrollierter Abbruch eines Threads implementiert werden kann.
 
Download:
TestThread2.java
Gegen über der Klasse TestThread1 verändern wir lediglich die stop()-Methode:
public synchronized void stop(){
  if (thread != null)
    thread = null;
}

Die Veränderung im Quelltext scheint minimal, inhaltlich passiert aber sehr viel. Satt die deprecated-Methode stop() aus der Klasse Thread zu benutzen, programmieren wir unsere stop() vollkommen neu. Wir halten den Thread dadurch an, dass wir ihm die Referenz entziehen und auf null setzen. Dies geschieht allerdings erst nach der Abfrage thread != null, die erst nach dem Abarbeiten der Thread.sleep(1000) erfolgt.

 

Download:
BeendenThread2. java
public class BeendenThread2 {
  static TestThread2 testThread = new TestThread2();
  
  public static void main (String[] args) 
                     throws InterruptedException {
    testThread.start();
    Thread.sleep(5000);
    testThread.stop();
  }
}
  Hier haben wir außer den Namen nichts verändert.
 
Ausgabe Thread gestartet
1
2
3
4
5
Thread gestoppt, Lebensdauer: 5017

C:\Java
Drücken Sie eine beliebige Taste . . .

 
interrupt() Wir ergänzen unsere Klasse TestThread um eine Methode interrupt(), die ihrerseits interrupt() der Klasse Thread aufruft.
 
Download:
TestThread3. java
public class TestThread3 implements Runnable{

  Thread thread = null;
  
  public synchronized void start(){
    if (thread == null){
      thread = new Thread(this);
      thread.start();
    }
  }
  
  public synchronized void stop(){
      if (thread != null)
      thread = null;
  }
  
  public synchronized void interrupt(){
    if (thread != null)                
      thread.interrupt();              
  }                                    
  
  
  public void run(){
    long startZeit = System.currentTimeMillis();
    int i = 1;
    System.out.println("Thread gestartet");
    while (thread != null){
      try{
        Thread.sleep(1000);
        System.out.println(i++);
      }
      catch(InterruptedException e){
        System.err.println("Thread interrupted!");
        break;
      }
    }
    long dauer = System.currentTimeMillis()-startZeit;
    System.out.println("Thread gestoppt, Lebensdauer: " 
                       + dauer);
  }
  
}
Bemerkungen Die Veränderungen gegenüber TestThread2 sind gelb markiert.
 
Download:
BeendenThread3. java
public class BeendenThread3 {
  static TestThread3 testThread = new TestThread3();
  
  public static void main (String[] args) 
                     throws InterruptedException {
    testThread.start();
    Thread.sleep(5000);
    testThread.interrupt();
    testThread.stop();
  }
}
Ausgabe Thread gestartet
1
2
3
4
5
Thread gestoppt, Lebensdauer: 5007

C:\java
Drücken Sie eine beliebige Taste . . .
 
  Diese Ausgabe können wir erst in ihrer Tragweite erfassen, wenn wir die Zeile Thread.sleep(5000); durch Thread.sleep(5500); ersetzen.
 
Ausgabe Thread gestartet
1
2
3
4
5
Thread interrupted!
Thread gestoppt, Lebensdauer: 5508

C:\Java
Drücken Sie eine beliebige Taste . . .
 
Bemerkungen Im zweiten Fall (Thread.sleep(5500)) unterbrach testThread.interrupt()das Objekt testThread als seine run()-Methode, gearde den Aufruf Thread.sleep(1000) ausführte, was zu einer InterrptedException führte, die im catch-Konstrukt bearbeitet wurde. Im ersten Fall (Thread.sleep(5000)) unterbrach testThread.interrupt() das Objekt testThread als seine run()-Methode gerade nicht den Aufruf Thread.sleep(1000); ausführte.
 
Noch eine Veränderung

Downoad:
BeendenThread4. java

public class BeendenThread4 {
  static TestThread3 testThread = new TestThread3();
  
  public static void main (String[] args) 
                     throws InterruptedException {
    testThread.start();
    Thread.sleep(5000);
    testThread.interrupt();
  }
}
Bemerkungen Gegenüber BeendenThread3 haben wir testThread.stop(); weggelassen. ThestThread3 wurde unverändert gelassen.
 
  Thread gestartet
1
2
3
4
5
Thread interrupted!
Thread gestoppt, Lebensdauer: 5007

C:\Java
Drücken Sie eine beliebige Taste . . .

 
  Jetzt kommt auch bei Thread.sleep(5000); die Ausgabe Thread interrupted! Das verwundert aber nicht. Der Thread lebt noch, er war nur unterbrochen und deshalb wirft sleep() eine InterruptedException. Die run()-Methode beendet den Thread dann auch, was man an der Ausgabe Thread gestoppt, Lebensdauer: 5007 erkennt. Denn zu dieser Ausgabe gelangt die run()-Methode erst, wenn die while-Schleife beendet ist, und das wiederum ist erst der Fall wenn thread != null falsch  und damit thread == null wahr ist.
zur Startseite www.pohlig.de  (C) MPohlig 2003