| Threads in Java |
|
|
Was sind Threads?Threads sind eine spezielle Art von Unterprozessen. Der Unterschied zu gewöhnlichen Unterprozessen ist, dass sie parallel zum Hauptprogramm und anderen Threads abgearbeitet werden. Durch das parallele Abarbeiten von unterschiedlichen Programmteilen können Programme sehr komplex werden. Threads machen Programme nichtdeterministisch, was bedeutet, dass nicht mehr vorhersehbar ist, in welchem Zustand sich das Programm nach einer bestimmten Zeit und evtl nach Eingabe bestimmter Daten befindet. Auf der anderen Seite soll erwähnt werden, dass Programme duch Threads sehr effizient werden können und deshalb oft benutzt werden. Ein weiteres Unterscheidungsmerkmal zu gewöhnlichen Unterprozessen ist, dass Threads zur Laufzeit Zugriff auf Eigenschaften des Objekts haben, das sie erzeugt hat. Um die Komplexität im Umgang mit Threads in den Griff zu bekommen stellt die Klasse Thread aus dem Package java.lang Synchronisations-Methoden zur Verfügung, von denen im Folgenden einige hier vorgestellt werden.
Implementierung einer Thread-KlasseDie Klasse Thread implementiert das Interface Runnable und damit dessen abstrakte Methode run(). Die run()-Methode implementiert die eigentliche Funktionalität des Threads. Um einen Thread auszuführen, muss das Hauptprogramm ein Objekt der Thread-Klasse angelegen und von diesem Objekt die Methode start() aufrufen. Dadurch wird dann run() aufgerufen.
ThreadKlasse thread = new ThreadKlasse(); thread.start();
class ThreadKlasse extends Thread {
public void run() {
while(true) {
// hier der Code, den der Thread ausführen soll
}
}
}
class ThreadKlasse implements Runnable {
public void run() {
while(true) {
// hier der Code, den der Thread ausführen soll
}
}
}
Abbruch eines ThreadsAbarbeitung der Methode run() Im einfachsten Fall wird ein Thread beendet, wenn die run()-Methode erfolgreich bis zum Ende ausgeführt wurde oder zwischendurch eine Exception auftritt. Abbruch mit der Methode interrupt() Weil in Threads oft Endlosschleifen benutzt werden, zB wenn sie Server-Aufgaben erledigen, braucht man zusätzliche Möglichkeiten, um einen Thread von Aussen stoppen zu können. Eine Möglichkeit bietet die Methode interrupt(). Im folgenden Beispiel wird bei jedem erneuten Eintritt in die Endlosschleife abgefragt, ob vom Hauptprogramm ein Interrupt aufgerufen wurde. Die main()-Methode erzeugt in diesem Fall als Hauptprogramm einen Thread und ruft die interrupt()-Methode auf.
class ThreadKlasse extends Thread {
public void run()
{
while (!isInterrupted()) {
// hier der Code des Threads...
}
System.out.println( "Der Thread wurde beendet!" );
}
public static void main(String args[]) {
ThreadKlasse thread = new ThreadKlasse();
thread.interrupt();
}
}
Auf dies Weise wird der Thread nicht während der Ausführung seines Codes unterbrochen, sondern kann seine laufende Arbeit erst beenden, bevor er abbricht. Abbruch mit der Methode stop() Eine unsaubere, aber manchmal unumgängliche Möglichkeit, einen Thread von Aussen zum Abbruch zu zwingen ist die Methode stop(). Diese Methode sollte sehr sparsam eingesetzt werden, weil sie einen Thread während der Arbeit unterbricht, was zu weiteren Programm-Fehlern führen kann.
public static void main(String args[]) {
ThreadKlasse thread = new ThreadKlasse();
thread.stop();
}
SynchronisationEs kann in Programmen sogenannte kritische Abschnitte geben, die auf keinen Fall unterbrochen werden dürfen. Damit Threads atomare (untrennbare) Operationen durchführen können, kann das Schlüsslewort synchronized vor eine Methoden geschrieben werden. Solange sich der Thread mit der Ausführung dieser Methode beschäftigt, darf und kann er nicht unterbrochen werden. Weil das Benutzen von synchronized die Geschwindigkeit eines Programmes erheblichen verringern kann, sollte man damit sparsam umgehen.
synchronized void kritischeFkt() {
atomareOperation1();
atomareOperation2();
}
Wenn nur die Operationen auf ein bestimmtes Objekt atomar sein sollen, kann die Methode synchronized(Object monitor) benutzt werden. monitor ist hierbei ein gemeinsam genutztes Objekt. Dann können keine anderen Programmteile dieses Objekt benutzen.
synchronized(Object monitor) {
monitor.operation1();
monitor.operation2();
}
Benachrichtigungen in kritischen BereichenManchmal spielt die Reihenfolge, in der Threads auf gemeinsam genutzte Objekte zugreifen, eine Rolle. In diesem Fall kann es unumgänglich sein, dass ein Thread auf die Ausführung eines anderen Threads warten muss. Wenn sich aber ein wartender Thread schon in einem synchronisierten Block befindet und somit den Zugriff auf das Objekt monitor blockiert, dann entsteht ein Deadlock, weil der Thread, der vor ihm an der Reihe gewesen wäre, bald darauf warten wird, dass monitor freigegeben wird. Eine Abhilfe dagegen schafft die Methode wait(). Diese Methode kann von dem Thread, der noch auf die Ausführung eines anderen Threads warten muss, aufgerufen werden. Durch wait() wird monitor vorläufig wieder freigegeben. Folgendes Beispiel zeigt den Zugriff desjenigen Threads, der auf die Bearbeitung eines anderen Threads warten muss.
// Code von Thread 2
synchronized(Object monitor) {
if (!thread1Fertig()) {
monitor.wait();
}
monitor.operation3();
monitor.operation4();
}
Damit der wartende Thread irgendwann wieder weiter machen kann, muss der Thread, auf den gewartet wird, nachdem er fertig ist, die Methode notify() aufrufen.
// Code von Thread 1
synchronized(Object monitor) {
monitor.operation1();
monitor.operation2();
monitor.notify();
}
|

