![]() |
|
|||||
Listing 9.2 FirstThread.java, Teil 2 public class FirstThread { public static void main( String args[] ) { Thread t1 = new Thread( new DateThread() ); t1.start(); Thread t2 = new Thread( new CounterThread() ); t2.start(); } } Beim Starten des Programms erfolgt eine Ausgabe auf dem Bildschirm, die etwa so aussehen kann: Thu Jan 18 15:40:20 CET 2001 0 1 2 3 4 5 6 7 8 9 Thu Jan 18 15:40:20 CET 2001 10 ... Deutlich ist die Verzahnung der beiden Prozesse zu erkennen. Was allerdings auf den ersten Blick etwas merkwürdig wirkt, ist die erste Zeile des Datum-Threads und viele weitere Zeilen des Zähl-Threads. Dies hat jedoch nichts zu sagen und zeigt deutlich den Nichtdeterminismus bei Threads. Interpretiert werden kann dies jedoch durch die unterschiedlichen Zeiten, die für die Aufgaben »gib Datum aus« und »gib Zahl« nötig sind. Automatisches Starten während der ErzeugungErst der Aufruf der start()-Methode lässt den Thread ablaufen. Etwas eleganter ist der Weg, dass das Objekt seinen eigenen Thread verwaltet, der im Konstruktor gestartet wird. Dann muss nur dort ein Thread-Objekt für die Runnable-Umgebung angelegt und die start()-Methode ausgeführt werden.
class DateThreadAutoStart implements Runnable { DateThreadAutoStart() { new Thread( this ).start(); } public void run() { for ( int i = 0; i < 20; i++ ) System.out.println( new Date() ); } } 9.2.3 Die Klasse Thread erweitern
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Wir bilden eine Unterklasse von Thread. |
| Für ein Programmstück, das nebenläufig ablaufen soll, überschreiben wir die run()-Methode. run() darf keinen Parameter enthalten, wenn doch, stimmt die Signatur nicht mit der in der Oberklasse Thread überein und statt des Überschreibens gäbe es ein Überladen. Dann aber geschieht beim Starten des Threads nichts, weil die geerbte parameterlose (und leere) run()-Methode aufgerufen wird. |
| Damit der Thread und damit das Programm startet, rufen wir die Objektmethode start() auf. Sie veranlasst die Laufzeitumgebung, Ressourcen zur Verfügung zu stellen. start() ruft dann automatisch run() auf. start() kann von uns auch überschrieben werden, was aber nur selten sinnvoll beziehungsweise nötig ist. Wir müssen dann darauf achten, super.start() aufzurufen, damit der Thread wirklich losrennt. |

Hier klicken, um das Bild zu Vergrößern
Beispiel Ein paralleles Programm, das das Datum ausgibt:
class DateThreadExtends extends Thread { public void run() { for ( int i = 0; i < 20; i++ ) System.out.println( new Date() ); } } |
Der Startschuss fällt für den Thread wieder beim Aufruf von start().
Thread t = new DateThreadExtends(); t.start();
Oder auch ohne Zwischenspeicherung der Objektreferenz:
new DateThreadExtends().start();
Damit wir als Thread-Benutzer nicht erst die start()-Methode aufrufen müssen, kann ein Thread sich auch wieder selbst starten. Der Konstruktor enthält einfach den Methodenaufruf start().
class DateThreadExtends extends Thread { DateThreadExtends() { start(); } ... der Rest bleibt ... }
class java.lang.Thread implements Runnable |
| void run() Diese Methode enthält den parallel auszuführenden Programmcode. |
| void start() Ein neuer Thread - neben dem die Methode aufrufenden Thread - wird gestartet. Der neue Thread führt die run()-Methode nebenläufig aus. Läuft der Thread schon, wird eine IllegalThreadStateException ausgelöst. |
Ein Programmierfehler, der hin und wieder passiert, ist Folgender: Anstatt start() rufen wir aus Versehen run() auf. Was geschieht? Fast genau das Gleiche wie bei start(), nur mit dem Unterschied, dass die Objektmethode run() nicht parallel zum übrigen Programm abgearbeitet wird. Der aktuelle Thread bearbeitet die run()-Methode sequenziell, bis sie zu Ende ist und die Anweisungen nach dem Aufruf an die Reihe kommen. Der Fehler fällt nicht immer direkt auf, denn die Aktionen in run() finden ja statt - nur nicht nebenläufig.
Eine Erweiterung der Klasse Thread hat den Vorteil, dass alle geerbten Methoden sofort genutzt werden können. Wenn wir Runnable implementieren, dann genießen wir den Vorteil nicht. Wie lassen sich dann trotzdem die Thread-Methoden nutzen? Dazu bietet Thread die Klassenmethode currentThread() an, um die Objektreferenz für das Thread-Exemplar zu holen, das gerade diese Methode ausführt.
Beispiel Die aktuelle Priorität des laufenden Threads soll ausgegeben werden.
System.out.println( Thread.currentThread().getPriority() ); |
Eine unangenehme Begleiterscheinung des Erweiterns von Thread ist, dass die Möglichkeit der Vererbung nicht genutzt werden kann, wenn die neue Thread-Unterklasse noch weitere Funktionalität erben soll - Mehrfachvererbung ist in Java nicht möglich. Dies ist häufig das Dilemma, wenn ein Applet nebenläufige Programmstücke besitzt. Hier können allerdings innere Klassen das Problem lösen. Die Tabelle fasst die Vor- und Nachteile der beiden Alternativen noch einmal zusammen:
| Vorteil | Nachteil | |
|---|---|---|
| Ableiten von Thread (extends Thread) | Programmcode in run() kann die Methoden der Klasse Thread nutzen. | Da es in Java keine Mehrfachvererbung gibt, kann die Klasse nur Thread erweitern. |
| Implementieren von Runnable
(implements Runnable) |
Die Klasse kann von einer anderen, problemspezifischen Klasse erben. | Kann sich nur mit Umwegen selbst starten; allgemein: Thread-Methoden können nur über Umwege genutzt werden. |
Tabelle 9.2 Vor- und Nachteile von extends Thread und implements Runnable
| << zurück |
Copyright (c) Galileo Press GmbH 2004
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.