Programmieren mit Threads

Java erlaubt das Erzeugen und Verwalten von Threads mit Hilfe der Systemklasse Thread. Beim Starten einer Javaanwendung bekommt die Methode main() automatisch einen Thread erzeugt und zugewiesen der sie ausführt. Mit Hilfe der Klasse Thread kann man selbst zusätzliche Threads erzeugen und starten.

Erzeugen und Start mit der Klasse Thread

Man kann einen neuen Thread starten indem man ein Objekt von Thread erzeugt. Hiermit wird parallel im Hintergrund ein Javathread erzeugt. Das Aufrufen der Methode start() startet dann den neuen Thread. Dies geschieht wie im folgenden Beispiel gezeigt:

public static void main(String[] args {
   ...
   Thread t1= new Thread(...);
   t1.start();
   ...
}

Im Laufzeitsystem wird hierdurch zuerst ein neuer Thread erzeugt und dann gestartet:

 

Es verbleibt die Frage welchen Programmcode der neue Thread ausführt.

Der ausgeführte Programmcode steht in einer Methode mit den Namen run() und muss von einer Klasse nach den Vorgaben der Schnittstelle Runnable implementiert werden.

Hierfür muss beim Erzeugen des Thread-objekts eine Referenz auf ein Objekt mitgegeben werden welches diese Schnittstelle implementiert. Geschieht dies nicht wird die Methode run() des Tread-objekts aufgerufen. Hierdurch ergeben sich zwei Möglichkeiten einen eigenen Thread mit einem bestimmten Programm zu starten:

Starten eines Threads durch Erweitern der Klasse Thread

Die erste Möglichkeit besteht im Erweitern der Klasse Thread und im überschreiben der Methode run().
Die Klasse Thread implementiert schon selbst die Schnittstelle Runnable. Ruft man die Methode start() ohne einen Parameter auf, so wird bei einer angeleiteten Klasse die überschriebene Methode run() in einem eigenen neuen Thread aufgerufen.
Im unten aufgeführten Beispiel wurde die Klasse myThread aus der Klasse Thread abgeleitet:

"Klassenhierachie Java Thread"

Die Klasse myThread verwaltet die Threads in ihrer main() Methode. Das Erzeugen und Starten der Threads der Klasse myThread könnte auch aus jeder anderen beliebigen Klasse erfolgen.

package s2.thread;

/**
*
* @author s@scalingbits.com
*/
public class MyThread extends Thread {
   @Override
   public void run() {
      System.out.println("Hurra ich bin myThread in einem Thread mit der Id: "
      + Thread.currentThread().getId());
   }

   public static void main(String[] args) {
      System.out.println("Start MyThread.main() Methode im Thread mit der Id: "
         + Thread.currentThread().getId());
      MyThread t1 = new MyThread();
      t1.start();
      System.out.println("Ende MyThread.main() Methode im Thread mit der Id: "
         + Thread.currentThread().getId());
      MyThread t2 = new MyThread();
      // t2 ist zwar ein Threadobjekt und repräsentiert einen Thread
      // da das Objekt nicht mit start() aufgerufen läuft es im gleichen
      // Thread wie die main() Routine!
      t2.run();
   }
} // Ende der Klasse

Das Programm erzeugt die folgende Konsolenausgabe:

Start MyThread.main() Methode im Thread mit der Id: 1
Ende MyThread.main() Methode im Thread mit der Id: 1
Hurra ich bin myThread in einem Thread mit der Id: 1
Hurra ich bin myThread in einem Thread mit der Id: 9

Im Beispielprogramm wird für die Referenz t1 ein neues Threadobjekt erzeugt. Anschliesend wird es durch seine start() Methode in einem eigenen Thread gestartet. Die run() Methode wird nach dem Starten im eigenen Thread ausgeführt.

Das Objekt mit der Referenz t2 ist zwar auch ein Thread. Das es aber direkt mir der run() Methode aufgerufen wird, läuft es im gleichen Thread wie die main() Methode.

Im UML Sequenzdiagramm ergibt sich der folgende Ablauf:

Starten eines Threads durch Implementieren der Schnittstelle Runnable

Das Erweitern einer Klasse aus der Klasse Thread ist nicht immer möglich. Man kann einen Thread auch starten indem man ein Thread-objekt erzeugt und ihm die Referenz auf eine Instanz der Schnittstelle Runnable mitgibt. Die Programmierabfolge ist dann:

  • Erzeugen eine Threadobjekts mit Referenz auf eine Instanz von Runnable.
  • Aufrufen der start() Methode des Threadobjekts
    • die Methode run() der Runnable-objekts wird automatisch aufgerufen

Die Klasse myRunnable:

package s2.thread;
/**
 *
 * @author s@scalingbits.com
 */
public class MyRunnable implements Runnable {
@Override
public void run() {
        System.out.println("Hurra ich bin myRunnable in einem Thread mit der Id: "
                + Thread.currentThread().getId());
    }
}

 

Die Klasse ThreadStarter die als Hauptprogramm dient:  

package s2.thread;
/**
*
* @author s@scalingbits.com
*/
public class ThreadStarter{
   public static void main(String[] args) {
      System.out.println("Start ThreadStarter.main() Methode im Thread mit der Id: "
         + Thread.currentThread().getId());
      MyRunnable r1 = new MyRunnable();
      MyRunnable r2 = new MyRunnable();
      Thread t1 = new Thread(r1);
      Thread t2 = new Thread(r2);
      t1.start();
      System.out.println("Ende ThreadStarter.main() Methode im Thread mit der Id: "
         + Thread.currentThread().getId());
      // r2 ist zwar ein Runnableobjekt , da das Objekt aber nicht von einem
      // Threadobjekt indirekt aufgerufen wirdläuft es im gleichen
      // Thread wie die main() Routine!
      r2.run();
   }
}

Das Programm erzeugt die gleichen Ausgaben wie das vorherige Programm:

Start ThreadStarter.main() Methode im Thread mit der Id: 1
Ende ThreadStarter.main() Methode im Thread mit der Id: 1
Hurra ich bin myRunnable in einem Thread mit der Id: 1
Hurra ich bin myRunnable in einem Thread mit der Id: 9

Das Aufrufen von r2.run() startet keinen eigenen Thread. Der Vorteil der Benutzung der Schnittstelle Runnable liegt darin, dass man die Methode run() in jeder beliebigen Klasse implementieren kann.

Die wichtigsten Methoden der Klasse Thread

  • Konstruktor: Thread(Runnable target) : Erzeugt einen neuen Thread und übergibt ein Objekt dessen run() Methode beim Starten des Threads aufgerufen (anstatt die eigene run() Methode aufzurufen.
  • static Thread currentThread(); liefert den aktuellen Thread der ein Codestück gerade ausführt
  • long getId(): Liefert die interne Nummer des Threads
  • join(): Hält den aktuellen Thread an bis der referenzierte Thread beendet ist.
  • static void sleep(long millis): Lässt den aktuellen Thread eine Anzahl von Millisekunden schlafen.
  • start(): Lässt die VM den referenzierten Thread starten. Dieser ruft dann die run() Methode auf.