Unsere Datenschutzrichtlinie wird in Kürze aktualisiert. Bitte sehen Sie sich die Vorschau an.

Java 7 Grundkurs

Die Klasse "Thread"

Testen Sie unsere 2017 Kurse

10 Tage kostenlos!

Jetzt testen Alle Abonnements anzeigen
In diesem Video lernen Sie die wichtigsten Methoden der Klasse "Thread" kennen. Sie ist zuständig für die gesamte Initialisierung und Ausführung parallel laufender Prozesse. Auch hier unterstützt Sie die Java-Dokumentation mit einer umfangreichen Beschreibung aller Methoden.
09:29

Transkript

Das Kernstück der Entwicklung nebenläufiger Anwendungen mit Java ist die Klasse Thread. Hier liegt die ganze Magie der Initialisierung und Ausführung parallel laufender Prozesse verborgen. Ich habe hier ein einfaches Beispielprojekt, das die grundsätzliche Funktionsweise demonstriert. Es enthält zwei Klassen: eine Main Class, die heißt Thread Demo und eine Klasse Arbeiter. Hier befindet sich die eigentliche Funktionalität, die im parallelen Prozess ausgeführt werden soll. Damit diese Funktionalität im parallelen Prozess ausgeführt werden kann, muss diese Klasse das Interface Runnable implementieren. Dieses Interface besteht aus einer Methode, und die heißt Run. Hier hinein kommt wie gesagt das, was im parallelen Prozess dann ausgeführt werden soll. Um einen solchen Prozess erzeugen zu können, benötigen wir nun diese Klasse Thread. Den Thread übergeben wir im Konstruktor einer Arbeiterinstanz. Dann können wir dem Thread noch einen Namen vergeben, was sich z. B. für das Debugging ganz gut macht. Mit Start starten wir den Thread. Nun kümmert sich das Thread-Objekt darum, einen neuen Prozess zu eröffnen, der parallel zu dieser Main-Methode ausgeführt wird. In diesem parallelen Prozess wird dann von unserem Arbeiter die Run-Methode aufgerufen. Ist der Thread einmal gestartet, muss ich nichts weiter tun, er läuft dann parallel zu diesem Programmfaden. Das macht er so lange, bis die Run-Methode unseres Arbeiters an ihrem Ende angekommen ist; dann ist auch der parallele Prozess an seinem Ende angekommen. Grafisch sieht das Ganze so aus: Wenn ich ein Thread-Objekt mit New erzeuge, dann ist es im Zustand "neu". Es gibt noch keinen parallel laufenden Prozess, es gibt lediglich dieses Java-Objekt. Um diesen parallelen Prozess in Gang zu setzen muss ich, wie wir gerade gesehen haben, vom Thread die Methode Start aufrufen. Das führt nun aber nicht dazu, dass der Prozess sofort losläuft, sondern er kommt erst mal in einen Zustand, der heißt "lauffähig". Es gibt nämlich in der Java-Laufzeitumgebung einen zentralen Prozessverwalter, einen "Scheduler", und der entscheidet, welcher Prozess laufen darf und welcher nicht. Wenn ein Programmfaden läuft, dann heißt das ja, dass er auf einem real existierenden Prozessor ausgeführt werden muss. Bei all der Virtualisierung muss natürlich trotzdem irgendjemand irgendwo die Arbeit machen. Die Anzahl der real zur Verfügung stehenden Prozessoren bzw. Prozessorkerne ist in der Regel eher klein. Die Zahl der Threads, die gerne laufen möchten, kann aber durchaus sehr groß sein. Dieser Prozessverwalter teilt nun jedem Thread, der gerne laufen möchte, eine gewisse Zeitscheibe zu, 20 Millisekunden etwa. Innerhalb dieser Zeitscheibe ist der jeweilige Thread im Zustand "läuft". Und wenn die Zeit abgelaufen ist, dann wird der Thread wieder unterbrochen, wird wieder zurückgepackt in die Kiste mit den lauffähigen Prozessen, und es wird ein anderer herausgenommen, der jetzt in den Zustand "läuft" kommt. Unser Thread springt also zwischen diesen beiden Zuständen hin und her. Und zwar so lange, bis die Run-Methode des Arbeiters an ihrem Ende angekommen ist. Dann geht der Thread in seinen nächsten Zustand über, nämlich "tot". Ein Thread ist also ein Einweg-Objekt. Wenn er gestartet wurde, dann läuft er los, so lange, bis er am Ziel angekommen ist, und dann ist er verbraucht. Möchte ich den Vorgang erneut starten, muss ich mir ein neues Thread-Objekt erzeugen. Einen Thread kann man also nur einmal starten. Ich kann die Start-Methode nur einmal aufrufen. Versuche ich sie ein zweites Mal aufzurufen vom selben Thread-Objekt, egal, ob er noch läuft oder schon am Ende angekommen ist, dann bekomme ich eine Exception. Gehen wir nun zurück in unser Programm und erweitern es ein bisschen. Unsere Main-Methode, in der Klasse Thread Demo, sieht momentan so aus, dass sie zwei Threads erzeugt, sie benamst, sie startet, vor dem Starten eine kurze Meldung ausgibt und nach dem Starten ebenfalls. Wenn wir das Ganze ausführen, dann sehen wir, dass beide Meldungen direkt hintereinander folgen, und erst danach kommen die Meldungen der beiden anderen Prozesse. Wir ändern das nun so ab, dass die Main-Methode wartet, bis die anderen beiden Prozesse fertig sind, bevor sie selbst ihre Fertigmeldung ausgibt. Dazu gibt es in der Klasse Thread eine Methode, die heißt Join. Das sieht dann also so aus: Die beiden Threads heißen "Erster" und "Zweiter", und ich rufe von diesen beiden Join auf. Diese Join-Methode blockiert jetzt und wartet, bis der Thread "Erster" fertig ist. Das heißt, die Main-Methode bleibt in dieser Zeile stehen. Der Main-Programmfaden sitzt also einfach da und wartet. Nun ist es zumindest prinzipiell möglich, das ein anderer von den parallel laufenden Prozessen diesen Main-Thread in seinem Warten unterbricht. In einem solchen Fall würde Join eine Interrupted Exception werfen, und das Warten hätte quasi ein vorzeitiges Ende. Diese Interrupted Exception ist nun aber eine Checked Exception, das heißt, wir müssen die hier fangen, an dieser Stelle. Ich lasse mir das mal generieren. Surround Statement with try-catch, und dann sieht das so aus. Diesen Logger-Eintrag hier können wir mal herausnehmen. Abgesehen von einigen Spezialfällen kann diese Interrupted Exception nur auftreten, wenn wir sie selbst aus einem anderen Prozess heraus auslösen. Da wir das nicht tun, gehen wir hier in diesem Beispiel davon aus, dass wir sie nicht behandeln müssen. Nun wollen wir aber nicht nur auf "Erster" warten, sondern auch auf "Zweiter". Und wenn wir jetzt das Programm erneut ausführen, dann sehen wir, dass die Meldung Fertig tatsächlich erst am Ende kommt. Es gibt noch eine weitere Möglichkeit, die Ausführung eines Threads vorübergehend anzuhalten. Und das ist die Methode Sleep. Das machen wir mal hier in unserem Arbeiter. Sleep ist static, das heißt, ich rufe sie über den Namen der Klasse auf. Als Parameter gebe ich die Anzahl der Millisekunden an, die wir jetzt hier an dieser Stelle gerne schlafen möchten. Sagen wir mal 200 Millisekunden. Auch Sleep kann wieder eine Interrupted Exception werfen, Sie kennen das ja nun schon. try-catch drum herum, diese Zeile entfernen mit Strg+E. Der Arbeiter wartet nun also in jedem Schleifendurchlauf 0,2 Sekunden, bevor er zum nächsten Durchlauf der Schleife weitergeht. Starten wir das Programm, dann sehen wir, geht alles schon ein bisschen langsamer. Es gibt einen wesentlichen Unterschied zwischen diesen beiden Methoden: Mit Join warte ich auf einen anderen Prozess, mit Sleep hingegen lege ich immer diesen Prozess schlafen, der diese Methode gerade aufruft. Ein Thread kann also niemals einen anderen Thread schlafen legen, sondern immer nur sich selbst. Ergänzen wir also unsere Grafik um die neuen Methoden Sleep und Join, führen den Thread in einen Zustand wartend, und aus diesem Zustand kommt er nicht etwa wieder ins "läuft" zurück, sondern natürlich nur ins "lauffähig". Es könnte ja sein, oder es ist sogar sehr wahrscheinlich, dass in diesem Moment gerade ein anderer Prozess am Laufen ist. Wir hatten ja vorhin gesagt, jeder Prozess bekommt vom Scheduler eine bestimmte Zeitscheibe zugeteilt, während der er seine Arbeit verrichten kann. Nun kann es aber zum Beispiel sein, dass dieser Prozess, der gerade läuft, gerade eine Datenbankanfrage losgeschickt hat, und bevor die Antwort zurückkommt, kann er eigentlich nichts weiter tun. Das heißt, er benötigt den Rest seiner Zeitscheibe eigentlich gerade nicht. Das können wir dem Scheduler über die Methode Yield mitteilen. Auch diese Methode ist eine statische Methode. Ein Prozess kann also immer nur den Rest seiner eigenen Zeitscheibe zurückgeben. Wenn wir in die Java-Doc der Klasse Thread hineinschauen, dann sehen wir dort noch viel mehr Methoden. Insbesondere gibt es neben der Methode Start zum Beispiel eine Methode Stop. Die ist allerdings Deprecated, das heißt veraltet. Man sollte also in neu geschriebenen Programmen vermeiden, sie zu verwenden. Aber nicht nur das, hier steht: "Diese Methode ist böse. Niemals aufrufen. Keinesfalls." So wie die Methode ursprünglich mal gedacht war, nämlich zum Anhalten eines laufenden Threads, funktioniert sie nicht. Wenn Sie mehr darüber wissen möchten, dann können Sie diesem Link hier folgen am Ende und einen längeren Artikel lesen, in dem das alles genauer erklärt wird. Das Gleiche gilt für die Methode Suspend, die ursprünglich mal einen Thread pausieren sollte. Und die Methode Resume, die diesen pausierten Thread wieder aufnehmen sollte. Alle drei Methoden nicht verwenden. Schließlich gibt es noch die Möglichkeit, einem Thread eine Priorität zu verpassen, mit der Methode Set Priority. Dafür sind sogar Konstanten definiert in dieser Klasse, und die heißen: MAX_PRIORITY, MIN_PRIORITY und NORM_PRIORITY. Wenn Sie die konkreten Werte wissen möchten, klicken Sie einfach drauf. Dann haben Sie hier bei MAX_PRIORITY einen Link zu Constant Field Values, da sind alle Konstanten aus allen möglichen Klassen aufgelistet. Und hier sehen wir dann, in Klasse Thread: MAX_PRIORITY ist 10, MIN_PRIORITY ist 1 und NORM_PRIORITY ist 5. Theoretisch sollte der Scheduler einen Thread mit einer höheren Priorität bevorzugen. Das Problem ist nur, Sie können sich nicht unbedingt darauf verlassen, dass das auch die Auswirkung hat, die Sie gerne möchten. Das darunterliegende System muss nicht zwangsläufig zehn verschiedene Prioritätsstufen unterstützen. Wenn es zum Beispiel nur zwei Stufen unterstützt, niedrig und hoch, dann ist es völlig egal, ob zwei Threads die Priorität 6, 7, 8 oder 9 haben. Aus Sicht des darunter liegenden Systems haben sie beide die Priorität "hoch". Sie sollten das Funktionieren Ihrer Anwendung also nicht unbedingt von fein getuneten Thread-Prioritäten abhängig machen. Die letzte Methode in der Klasse Thread, die ich Ihnen zeigen möchte, heißt isAlive, das ist diese hier. Und mit der können wir herausfinden, ob ein Thread noch lebt. Schauen wir dazu nochmal auf unsere Grafik. Ein Thread lebt, wenn er gestartet wurde und noch nicht tot ist. Nun kennen Sie also die wichtigsten Methoden der Klasse Thread und haben gesehen, wie man diese verwenden kann.

Java 7 Grundkurs

Machen Sie sich mit den Grundlagen der Java-Programmierung vertraut und lernen Sie die Syntax der Sprache sowie das Konzept der objektorientierten Softwareentwicklung kennen.

8 Std. 32 min (66 Videos)
Derzeit sind keine Feedbacks vorhanden...
 

Dieser Online-Kurs ist als Download und als Streaming-Video verfügbar. Die gute Nachricht: Sie müssen sich nicht entscheiden - sobald Sie das Training erwerben, erhalten Sie Zugang zu beiden Optionen!

Der Download ermöglicht Ihnen die Offline-Nutzung des Trainings und bietet die Vorteile einer benutzerfreundlichen Abspielumgebung. Wenn Sie an verschiedenen Computern arbeiten, oder nicht den ganzen Kurs auf einmal herunterladen möchten, loggen Sie sich auf dieser Seite ein, um alle Videos des Trainings als Streaming-Video anzusehen.

Wir hoffen, dass Sie viel Freude und Erfolg mit diesem Video-Training haben werden. Falls Sie irgendwelche Fragen haben, zögern Sie nicht uns zu kontaktieren!