From 33613a85afc4b1481367fbe92a17ee59c240250b Mon Sep 17 00:00:00 2001 From: Sven Eisenhauer Date: Fri, 10 Nov 2023 15:11:48 +0100 Subject: add new repo --- .../hjp5/html/k100145.html | 692 +++++++++++++++++++++ 1 file changed, 692 insertions(+) create mode 100644 Master/Reference Architectures and Patterns/hjp5/html/k100145.html (limited to 'Master/Reference Architectures and Patterns/hjp5/html/k100145.html') diff --git a/Master/Reference Architectures and Patterns/hjp5/html/k100145.html b/Master/Reference Architectures and Patterns/hjp5/html/k100145.html new file mode 100644 index 0000000..ea6ff51 --- /dev/null +++ b/Master/Reference Architectures and Patterns/hjp5/html/k100145.html @@ -0,0 +1,692 @@ + + + +Handbuch der Java-Programmierung, 5. Auflage + + + + + + + + + +
 Titel  + Inhalt  + Suchen  + Index  + DOC  +Handbuch der Java-Programmierung, 5. Auflage +
 <<  +  <   +  >   + >>  + API  +Kapitel 22 - Multithreading +
+
+ + + + +

22.2 Die Klasse Thread

+
+ +
+ + + + +

22.2.1 Erzeugen eines neuen Threads

+ +

+Die Klasse Thread +ist Bestandteil des Pakets java.lang +und steht damit allen Anwendungen standardmäßig zur Verfügung. +Thread +stellt die Basismethoden zur Erzeugung, Kontrolle und zum Beenden +von Threads zur Verfügung. Um einen konkreten Thread zu erzeugen, +muss eine eigene Klasse aus Thread +abgeleitet und die Methode run +überlagert werden. + +

+Mit Hilfe eines Aufrufs der Methode start +wird der Thread gestartet und die weitere Ausführung an die Methode +run +übertragen. start +wird nach dem Starten des Threads beendet, und der Aufrufer kann parallel +zum neu erzeugten Thread fortfahren. +

+ + + + + + + + + + + +
+ +

+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, sondern wäre +ein normaler Methodenaufruf wie jeder andere und würde direkt +aus dem bereits laufenden Thread des Aufrufers erfolgen.

+ + + + +
 Warnung 
+
+ +

+Das folgende Beispiel zeigt einen einfachen Thread, der in einer Endlosschleife +einen Zahlenwert hochzählt: + + +

+ + + + + +
+ +
+001 /* Listing2201.java */
+002 
+003 class MyThread2201
+004 extends Thread
+005 {
+006   public void run()
+007   {
+008     int i = 0;
+009     while (true) {
+010       System.out.println(i++);
+011     }
+012   }
+013 }
+014 
+015 public class Listing2201
+016 {
+017   public static void main(String[] args)
+018   {
+019     MyThread2201 t = new MyThread2201();
+020     t.start();
+021   }
+022 }
+
+
+Listing2201.java
+ +Listing 22.1: Ein einfacher Thread mit einem Zähler

+ +

+Zunächst wird hier ein neues Objekt vom Typ MyThread2201 +instanziert. Die Ausführung eines Threads ist damit vorbereitet, +aber noch nicht tatsächlich erfolgt. Erst durch den Aufruf von +start +wird ein neuer Thread erzeugt und durch einen impliziten Aufruf von +run +der Thread-Body gestartet. Da das Programm in einer Endlosschleife +läuft, läßt es sich nur gewaltsam abbrechen (beispielsweise +durch Drücken von [STRG]+[C]). +

+ + + + + + + + + +
+ +

+Im Gegensatz zu unseren bisherigen Beispielen wird dieses Programm +nicht automatisch nach main +beendet. Eine Java-Applikation +wird nämlich immer nach Ende des letzten Threads beendet, der +kein Hintergrund-Thread (Dämon) +ist. Da ein einfaches Programm nur einen einzigen Vordergrund-Thread +besitzt (nämlich den, in dem main +läuft), wird es demnach beendet, wenn main +beendet wird. Das Beispielprogramm erzeugt dagegen einen zusätzlichen +Vordergrund-Thread und kann damit vom Interpreter erst dann beendet +werden, wenn auch dieser Thread beendet wurde. Durch Aufruf von exit +lassen sich auch Programme mit laufenden Vordergrund-Threads abbrechen.

+ + + + +
 Hinweis 
+
+ + + + +

22.2.2 Abbrechen eines Threads

+ +

+Zunächst einmal wird ein Thread dadurch beendet, dass das Ende +seiner run-Methode +erreicht ist. In manchen Fällen ist es jedoch erforderlich, den +Thread von außen abzubrechen. Die bis zum JDK 1.1 übliche +Vorgehensweise bestand darin, die Methode stop +der Klasse Thread +aufzurufen. Dadurch wurde der Thread abgebrochen und aus der Liste +der aktiven Threads entfernt. + +

+Wir wollen das vorige Beispiel erweitern und den Thread nach zwei +Sekunden durch Aufruf von stop +beenden: + + +

+ + + + + +
+ +
+001 /* Listing2202.java */
+002 
+003 class MyThread2202
+004 extends Thread
+005 {
+006   public void run()
+007   {
+008     int i = 0;
+009     while (true) {
+010       System.out.println(i++);
+011     }
+012   }
+013 }
+014 
+015 public class Listing2202
+016 {
+017   public static void main(String[] args)
+018   {
+019     MyThread2202 t = new MyThread2202();
+020     t.start();
+021     try {
+022       Thread.sleep(2000);
+023     } catch (InterruptedException e) {
+024       //nichts
+025     }
+026     t.stop();
+027   }
+028 }
+
+
+Listing2202.java
+ +Listing 22.2: Beenden des Threads durch Aufruf von stop

+ +

+An diesem Beispiel kann man gut erkennen, dass der Thread tatsächlich +parallel zum Hauptprogramm ausgeführt wird. Nach dem Aufruf von +start +beginnt einerseits die Zählschleife mit der Bildschirmausgabe, +aber gleichzeitig fährt das Hauptprogramm mit dem Aufruf der +sleep-Methode +und dem Aufruf von stop +fort. Beide Programmteile laufen also parallel ab. +

+ + + + + + + + + + + +
+ +

+Mit dem JDK 1.2 wurde die Methode stop +als deprecated +markiert, d.h. sie sollte nicht mehr verwendet werden. Der Grund dafür +liegt in der potentiellen Unsicherheit des Aufrufs, denn es +ist nicht voraussagbar und auch nicht definiert, an welcher Stelle +ein Thread unterbrochen wird, wenn ein Aufruf von stop +erfolgt. Es kann nämlich insbesondere vorkommen, dass der Abbruch +innerhalb eines kritischen Abschnitts erfolgt (der mit dem synchronized-Schlüsselwort +geschützt wurde) oder in einer anwendungsspezifischen Transaktion +auftritt, die aus Konsistenzgründen nicht unterbrochen werden +darf.

+ + + + +
 JDK1.1-6.0 
+
+ +

+Die alternative Methode, einen Thread abzubrechen, besteht darin, +im Thread selbst auf Unterbrechungsanforderungen zu reagieren. So +könnte beispielsweise eine Membervariable cancelled +eingeführt und beim Initialisieren des Threads auf false +gesetzt werden. Mit Hilfe einer Methode cancel +kann der Wert der Variable zu einem beliebigen Zeitpunkt auf true +gesetzt werden. Aufgabe der Bearbeitungsroutine in run +ist es nun, an geeigneten Stellen diese Variable abzufragen und für +den Fall, dass sie true +ist, die Methode run +konsistent zu beenden. + +

+Dabei darf cancelled natürlich +nicht zu oft abgefragt werden, um das Programm nicht unnötig +aufzublähen und das Laufzeitverhalten des Threads nicht zu sehr +zu verschlechtern. Andererseits darf die Abfrage nicht zu selten erfolgen, +damit es nicht zu lange dauert, bis auf eine Abbruchanforderung reagiert +wird. Insbesondere darf es keine potentiellen Endlosschleifen geben, +in denen cancelled überhaupt +nicht abgefragt wird. Die Kunst besteht darin, diese gegensätzlichen +Anforderungen sinnvoll zu vereinen. + +

+Glücklicherweise gibt es in der Klasse Thread bereits einige +Methoden, die einen solchen Mechanismus standardmäßig unterstützen: +

+ + + + + +
+ +
+public void interrupt()
+
+public boolean isInterrupted()
+
+public static boolean interrupted()
+
+
+
+java.lang.Thread
+ +

+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. Die statische Methode interrupted +stellt den Status des Abbruchsflags beim aktuellen Thread fest. +Ihr Aufruf entspricht dem Aufruf von currentThread().isInterrupted(), +setzt aber zusätzlich das Abbruchflag auf seinen initialen Wert +false +zurück. + +

+Wir wollen uns den Gebrauch dieser Methoden an einem Beispiel ansehen. +Dazu soll ein Programm geschrieben werden, das in einem separaten +Thread ununterbrochen Textzeilen auf dem Bildschirm ausgibt. Das Hauptprogramm +soll den Thread erzeugen und nach 2 Sekunden durch einen Aufruf von +interrupt +eine Unterbrechungsanforderung erzeugen. Der Thread soll dann die +aktuelle Zeile fertig ausgeben und anschließend terminieren. + + +

+ + + + + +
+ +
+001 /* Listing2203.java */
+002 
+003 public class Listing2203
+004 extends Thread
+005 {
+006   int cnt = 0;
+007 
+008   public void run()
+009   {
+010     while (true) {
+011       if (isInterrupted()) {
+012         break;
+013       }
+014       printLine(++cnt);
+015     }
+016   }
+017 
+018   private void printLine(int cnt)
+019   {
+020     //Zeile ausgeben
+021     System.out.print(cnt + ": ");
+022     for (int i = 0; i < 30; ++i) {
+023       System.out.print(i == cnt % 30 ? "* " : ". ");
+024     }
+025     System.out.println();
+026     //100 ms. warten
+027     try {
+028       Thread.sleep(100);
+029     } catch (InterruptedException e) {
+030       interrupt();
+031     }
+032   }
+033 
+034   public static void main(String[] args)
+035   {
+036     Listing2203 th = new Listing2203();
+037     {
+038       //Thread starten
+039       th.start();
+040       //2 Sekunden warten
+041       try {
+042         Thread.sleep(2000);
+043       } catch (InterruptedException e) {
+044       }
+045       //Thread unterbrechen
+046       th.interrupt();
+047     }
+048   }
+049 }
+
+
+Listing2203.java
+ +Listing 22.3: Anwendung der Methoden interrupt und isInterrupted

+ +

+Die main-Methode +ist leicht zu verstehen. Sie startet den Thread, wartet 2 Sekunden +und ruft dann die Methode interrupt +auf. In der Methode run +wird in einer Endlosschleife durch Aufruf von printLine +jeweils eine neue Zeile ausgegeben. Zuvor wird bei jedem Aufruf mit +isInterrupted +geprüft, ob das Abbruchflag gesetzt wurde. Ist das der Fall, +wird keine weitere Zeile ausgegeben, sondern die Schleife (und mit +ihr der Thread) beendet. + +

+Innerhalb von printLine wird +zunächst die Textzeile ausgegeben und dann eine Pause von 100 +Millisekunden eingelegt. Da in der Methode keine Abfrage des Abbruchflags +erfolgt, ist sichergestellt, dass die aktuelle Zeile selbst dann bis +zum Ende ausgegeben wird, wenn der Aufruf von interrupt +mitten in der Schleife zur Ausgabe der Bildschirmzeile erfolgt. + +

+Da die Pause nach der Bildschirmausgabe mit 100 Millisekunden vermutlich +länger dauert als die Bildschirmausgabe selbst, ist es recht +wahrscheinlich, dass der Aufruf von interrupt +während des Aufrufs von sleep +erfolgt. Ist das der Fall, wird sleep +mit einer InterruptedException +abgebrochen (auch wenn die geforderte Zeitspanne noch nicht vollständig +verstrichen ist). Wichtig ist hier, dass das Abbruchflag zurückgesetzt +wird 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 zu setzen und run +die Abbruchanforderung zu signalisieren. Alternativ hätten wir +auch die Ausnahme an den Aufrufer weitergeben können und sie +dort als Auslöser für das Ende der Ausgabeschleife betrachten +können. +

+ + + + + + + + + +
+ +

+Die beiden anderen Methoden, die eine Ausnahme des Typs InterruptedException +auslösen können, sind join +der Klasse Thread +und wait +der Klasse Object. +Auch sie setzen beim Auftreten der Ausnahme das Abbruchflag zurück +und müssen daher in ähnlicher Weise behandelt werden.

+ + + + +
 Hinweis 
+
+ + + + +

22.2.3 Anhalten eines Threads

+

+ + + + + + + + + + + +
+ +

+Die Klasse Thread +besitzt zwei Methoden suspend +und resume, +mit deren Hilfe es möglich ist, einen Thread vorübergehend +anzuhalten und anschließend an der Unterbrechungsstelle fortzusetzen. +Beide Methoden sind nicht ganz ungefährlich und können unbemerkt +Deadlocks verursachen. Sie wurden daher mit dem JDK 1.2 als deprecated +markiert und sollten nicht mehr verwendet werden. Ihre Funktionalität +muss - wenn erforderlich - manuell nachgebildet werden.

+ + + + +
 JDK1.1-6.0 
+
+ + + + +

22.2.4 Weitere Methoden

+ + + + +

sleep

+ +

+Sowohl innerhalb der Threads als auch innerhalb der Methode main +wird ein Aufruf von Thread.sleep +verwendet, um das Programm pausieren zu lassen. sleep +ist eine statische Methode der Klasse Thread, +die mit einem oder zwei Parametern aufgerufen werden kann: +

+ + + + + +
+ +
+public static void sleep(long millis)
+public static void sleep(long millis, int nanos)
+
+
+
+java.lang.Thread
+ +

+Die erste Version sorgt dafür, dass der aktuelle prozess für +die (in Millisekunden angegebene) Zeit unterbrochen wird. Die zweite +erlaubt eine noch genauere Eingabe der Wartezeit, indem auch Bruchteile +im Nanosekundenbereich angegeben werden können. In beiden Fällen +wird die tatsächlich erzielbare Genauigkeit allerdings durch +Hardwarerestriktionen der Zielmaschine begrenzt. Im Falle der aktuellen +SUN-JDKs unter Windows liegt sie bei etwa 1 ms und ist damit wesentlich +höher als currentTimeMillis. +In Abschnitt 16.3.5 finden +sich Beispielprogramme, mit denen die Auflösungen von currentTimeMillis +und sleep +ermittelt werden können. +

+ + + + + + + + + +
+ +

+Die Kapselung des Aufrufs von Thread.sleep +innerhalb eines try-catch-Blocks +ist erforderlich, weil sleep +während der Wartezeit eine Ausnahme vom Typ InterruptedException +auslösen kann. Ohne den try-catch-Block +würde diese an den Aufrufer weitergegeben werden.

+ + + + +
 Hinweis 
+
+ +

+Als Klassenmethode kann sleep +aufgerufen werden, ohne dass eine Instanz der Klasse Thread +verfügbar ist. Insbesondere kann die Methode auch dazu verwendet +werden, das Hauptprogramm pausieren zu lassen, das ja nicht explizit +als Thread erzeugt wurde. Dies funktioniert deshalb, weil beim Starten +eines Java-Programms automatisch ein Thread für die Ausführung +des Hauptprogramms angelegt wurde. + + + + +

isAlive

+ +

+Mit dieser Methode kann festgestellt werden, ob ein Thread noch läuft. +

+ + + + + +
+ +
+public final boolean isAlive()
+
+
+
+java.lang.Thread
+ +

+isAlive +gibt immer dann true +zurück, wenn der Thread gestartet, aber noch nicht wieder beendet +wurde. Beendet wird ein Thread, wenn das Ende der run-Methode +erreicht ist oder wenn (in Prä-1.2-JDKs) die Methode stop +aufgerufen wurde. + + + + +

join

+

+ + + + + +
+ +
+public final void join()
+  throws InterruptedException
+
+
+
+java.lang.Thread
+ +

+Die Methode join +wartet auf das Ende des Threads, für den sie aufgerufen wurde. +Sie ermöglicht es damit, einen prozess zu starten und (ähnlich +einem Funktionsaufruf) mit der weiteren Ausführung so lange zu +warten, bis der prozess beendet ist. join +gibt es auch mit einem long +als Parameter. In diesem Fall wartet die Methode maximal die angegebene +Zeit in Millisekunden und fährt nach Ablauf der Zeit auch dann +fort, wenn der prozess noch nicht beendet ist. +


+ + + +
 Titel  + Inhalt  + Suchen  + Index  + DOC  +Handbuch der Java-Programmierung, 5. Auflage, Addison +Wesley, Version 5.0.1 +
 <<  +  <   +  >   + >>  + API  +© 1998, 2007 Guido Krüger & Thomas +Stark, http://www.javabuch.de +
+ + + -- cgit v1.2.3