home | suche | kontakt/johner | institut studierende | tech-docs | mindmailer |
![]() |
Nebenläufigkeit
Als Nebenläufigkeit bezeichnet man die Fähigkeit eines Systems, zwei oder mehr Vorgänge gleichzeitig oder nebenläufig ("parallel") ausführen zu können. Dabei unterscheidet man von Multitasking und Multithreading. Von Multitasking spricht man, wenn auf einem Rechner mehrere Programme (Prozesse) "parallel" ausgeführt werden (Thread = Folge von Anweisungen). Durch Multithreading kann die Nebeläufigkeit innerhalb (!) eines Prozesses realisiert werden, zum Beispiel wartet die GUI auf einen Mausklick. Multithreading erlaubt es, dass das Programm mehrere CPUs nutzt.
Ein wichtiger Unterschied zwischen Threads und Prozessen ist der, dass alle Threads eines Programms sich einen gemeinsamen Adressraum teilen, also auf dieselben Variablen zugreifen, während die Adressräume unterschiedlicher Prozesse streng voneinander getrennt sind. Dabei hat jeder Thread einen eigenen Stack für das Anlegen von Variablen und zum Aufrufen von Methoden.
Threads teilen sich die Variablen und Objekte des Programms zu denen sie gehören. Eine echte parallele Ausführung von Prozessen kann nur mit mehreren Prozessoren durchgeführt werden.
Jedes Java-Programm hat einen Main-Thread, der beim Ausführen des Programms für die ausfphrung der main-Methode zuständig ist.
2.7.2 Realisierung von Multithreading in Java
Threads werden in Java durch die Klasse Thread und das Interfaces Runnable implementiert. In beiden Fällen wird der Thread-Body, also der prallel auszuführende Code, in Form der überlagerten Methode run zur Verfügung gestellt.
package parallel;
|
package parallel;
|

Nicht immer ist es möglich, eine Klasse, die als Thread laufen soll, von Thread abzuleiten. Dies ist insbesondere dann nicht möglich, wenn die Klasse Bestandteil einer Vererbungshierrachie ist, die eingentlich nichts mit Multithreading zu tun hat. Da Java keine Mehrfachvererbung kennt, kann eine bereits abgeleitete Klasse nicht von einer weiteren Klasse erben. Um trotzdem das Multithreading-Konzept nutzen zu können wird einfach das Interface Runnable implementiert. Dieses Interface besteht nur aus der run-Methode. Tatsächlich muss jede Klasse, deren Instanzen als Thread laufen sollen , das Interface Runnable implementieren (sogar die Klasse Thread selbst).
Um eine nicht von Thread abgeleitete Instanz als Thread laufen zu lassen, sind folgende Schritte zu befolgen.
- Zunächst ein Thread-Objekt erzeugen
- An den Konstruktor wird das Objekt übergeben das parallel ausgeführt werden soll
- Die Methode start des neuen Thread-Objekts aufrufen
Dieses Beispiel zeigt die Vorgehensweise:
package parallel;
|
Arbeiten mit Threads
Die Methode run sollte vom Programm niemals direkt aufgerufen werden. Um einen Thread zu starten, ist immer start aufzurufen! Dadurch wird der neue Thread erzeugt und initialisiert und ruft schließlich selbst run auf, um den Anwendungscode auszuführen. Ein direkter Aufruf von run würde dagegen keinen neuen Thread erzeugen, sonder wäre ein normaler Methodenaufruf wie jeder andere und würde direkt aus dem bereits laufenden Thread des Aufrufers erfolgen.
Es gibt 3 Möglichkeiten, dass ein Thread zuende ist
- das Ende der run()-Methode ist erreicht
- run() bricht mit Fehlern ab
- der Thread wird von außen mit interrupt() unterbrochen, allerdings nur wenn innerhalb des Threads eine Überprüfung stattfindet.
Durch Aufruf von interrupt wird ein Flag gesetzt, das eine Unterbrechungsanforderung signalisiert. Durch Aufruf von isInterrupted kann der Thread feststellen, ob das Abbruchflag gesetzt wurde und der Thread beendet werden soll.
package parallel;
|
Mit der while-Schleife wird überprüft, ob der Thread von außen noch nicht abgebrochen wurde.
Die Bildschirmausgabe in diesem Programm ist vermutlich deutlich kürzer, als die Pause nach der Bildschirmausgabe. Deswegen ist es recht wahrscheinlich, dass der Aufruf von interrupt während des Aufrufs von sleep erfolgt. Ist das der Fall, wird sleep mit einer Interrupted-Exception abgebrochen (auch wenn die geforderte Zeitspanne noch nicht vollständig verstrichen ist). Wichtig ist hier, dass das Abbruchflag von des Exception zurückgesetzt wird (isInterrupted liefert dann wieder false) und der Aufruf von interrupt somit eigentlich verlorengehen würde, wenn er nicht direkt in der catch-Klausel behandelt würde. Wir rufen daher innerhalb der catch-Klausel interrupt erneut auf, um das Flag wieder auf true zu setzen und run die Abbruchanforderung zu signalisieren.
Bei endlos laufenden Threads besteht die Möglichkeit über die Methode setDeamon(true) zu signalisieren, dass beim Beenden der main() der Thread ebenfalls beendet werden soll.
Thread infinity = new Thread(new InfinityCommand());
|
Synchronisierung von Threads
Zur Synchronisierung nebenläufiger Prozesse hat Java das Konzept des Monitors implementiert. Die Klasse Start gibt den beiden Threads einen gemeinsamen Monitor mit. Über diesen Monitor (vom Typ Object) können sich die beiden Threads mit Hilfe der Methoden wait() und notify() verständigen. Die Methoden wait() und notify() erbt jede Klasse von Object. Beide Methoden müssen in einem Block stehen, der synchornized ist.

Durch synchronized kann entweder eine komplette Methode oder ein Block innerhalb einer Methode geschützt werden. Das folgende Codebeispiel zeigt einen Thread, der einen Teil der run-Methode über das Monitor-Objekt synchonisiert. Das Monitor-Objekt wird ihm im Konstruktor übergeben. Ein anderer Thread, der mit diesem Thread zusammenarbeitet, muss das gleiche Objekt ebenfalls im Konstruktor übergeben bekommen.
package parallel2;
|
Synchronisierung von Threads mit Java 5
In Java 5 läuft die Synchronisation nicht mehr über ein normales Object, sondern über die spezielle Klassen java.util.concurrent.locks.Condition und java.util.concurrent.locks.Lock.
- Condition wird von Lock erzeugt
- condition.await() ist Pendant zu object.wait()
- condition.signal() ist Pendant zu object.notify()
signal() und wait() müssen im synchronisierten Block innerhalb des locks stehen. Die Synchronisation erfolgt über den Aufruf der Methode lock.lock() und beim Beenden lock.unlock().

package parallel3;
|