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/k100146.html | 524 +++++++++++++++++++++ 1 file changed, 524 insertions(+) create mode 100644 Master/Reference Architectures and Patterns/hjp5/html/k100146.html (limited to 'Master/Reference Architectures and Patterns/hjp5/html/k100146.html') diff --git a/Master/Reference Architectures and Patterns/hjp5/html/k100146.html b/Master/Reference Architectures and Patterns/hjp5/html/k100146.html new file mode 100644 index 0000000..db6956f --- /dev/null +++ b/Master/Reference Architectures and Patterns/hjp5/html/k100146.html @@ -0,0 +1,524 @@ + + + +Handbuch der Java-Programmierung, 5. Auflage + + + + + + + + + +
 Titel  + Inhalt  + Suchen  + Index  + DOC  +Handbuch der Java-Programmierung, 5. Auflage +
 <<  +  <   +  >   + >>  + API  +Kapitel 22 - Multithreading +
+
+ + + + +

22.3 Das Interface Runnable

+
+ +
+ +

+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 Vererbungshierarchie ist, die eigentlich +nichts mit Multithreading zu tun hat. Da Java keine Mehrfachvererbung +kennt, kann eine bereits abgeleitete Klasse nicht von einer weiteren +Klasse erben. Da sehr unterschiedliche Klassen als Thread parallel +zu vorhandenem Code ausgeführt werden können, ist dies eine +sehr unschöne Einschränkung des Multithreading-Konzepts +von Java. + +

+Glücklicherweise gibt es einen Ausweg. Er besteht darin, einen +Thread nicht durch Ableiten aus Thread, +sondern durch Implementierung des Interfaces Runnable +zu erzeugen. Runnable +enthält nur eine einzige Deklaration, nämlich die der Methode +run: +

+ + + + + +
+ +
+public abstract void run()
+
+
+
+java.lang.Runnable
+ + + + +

22.3.1 Implementieren von Runnable

+ +

+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 in dieser Weise als Thread laufen zu lassen, ist +in folgenden Schritten vorzugehen: +

+ +

+Nun startet das Thread-Objekt +die run-Methode +des übergebenen Objekts, das sie ja durch die Übergabe im +Konstruktor kennt. Da dieses Objekt das Interface Runnable +implementiert, ist garantiert, dass eine geeignete Methode run +zur Verfügung steht. + +

+Wir wollen dies an einem Beispiel deutlich machen: + + +

+ + + + + +
+ +
+001 /* Listing2204.java */
+002 
+003 class A2204
+004 {
+005   int irgendwas;
+006   //...
+007 }
+008 
+009 class B2204
+010 extends A2204
+011 implements Runnable
+012 {
+013    public void run()
+014    {
+015       int i = 0;
+016       while (true) {
+017          if (Thread.interrupted()) {
+018             break;
+019          }
+020          System.out.println(i++);
+021       }
+022    }
+023 }
+024 
+025 public class Listing2204
+026 {
+027    public static void main(String[] args)
+028    {
+029       B2204 b = new B2204();
+030       Thread t = new Thread(b);
+031       t.start();
+032       try {
+033          Thread.sleep(1000);
+034       } catch (InterruptedException e){
+035          //nichts
+036       }
+037       t.interrupt();
+038    }
+039 }
+
+
+Listing2204.java
+ +Listing 22.4: Implementieren von Runnable

+ +

+Die Klasse B2204 ist von A2204 +abgeleitet und kann daher nicht von Thread +abgeleitet sein. Statt dessen implementiert sie das Interface Runnable. +Um nun ein Objekt der Klasse B2204 +als Thread auszuführen, wird in main +von Listing2204 eine Instanz +dieser Klasse erzeugt und an den Konstruktor der Klasse Thread +übergeben. Nach dem Aufruf von start +wird die run-Methode +von B2204 aufgerufen. + + + + +

22.3.2 Multithreading durch Wrapper-Klassen

+ +

+Auf eine ähnliche Weise lassen sich auch Methoden, die ursprünglich +nicht als Thread vorgesehen waren, in einen solchen umwandeln und +im Hintergrund ausführen. Der Grundstein für die Umwandlung +eines gewöhnlichen Objekts in einen Thread wird dabei immer bei +der Übergabe eines Runnable-Objekts +an den Konstruktor des Thread-Objekts +gelegt. Das folgende Beispiel demonstriert, wie eine zeitintensive +Primfaktorzerlegung im Hintergrund laufen kann. + +

+Zunächst benötigen wir dazu eine Klasse PrimeNumberTools, +die Routinen zur Berechnung von Primzahlen und zur Primfaktorzerlegung +zur Verfügung stellt. Diese Klasse ist weder von Thread +abgeleitet, noch implementiert sie Runnable: + + +

+ + + + + +
+ +
+001 /* PrimeNumberTools.java */
+002 
+003 public class PrimeNumberTools
+004 {
+005   public void printPrimeFactors(int num)
+006   {
+007     int whichprime = 1;
+008     int prime;
+009     String prefix;
+010 
+011     prefix = "primeFactors("+num+")= ";
+012     while (num > 1) {
+013       prime = getPrime(whichprime);
+014       if (num % prime == 0) {
+015         System.out.print(prefix+prime);
+016         prefix = " ";
+017         num /= prime;
+018       } else {
+019         ++whichprime;
+020       }
+021     }
+022     System.out.println();
+023   }
+024 
+025   public int getPrime(int cnt)
+026   {
+027     int i = 1;
+028     int ret = 2;
+029 
+030     while (i < cnt) {
+031       ++ret;
+032       if (isPrime(ret)) {
+033         ++i;
+034       }
+035     }
+036     return ret;
+037   }
+038 
+039   private boolean isPrime(int num)
+040   {
+041     for (int i = 2; i < num; ++i) {
+042       if (num % i == 0) {
+043         return false;
+044       }
+045     }
+046     return true;
+047   }
+048 }
+
+
+PrimeNumberTools.java
+ +Listing 22.5: Eine Klasse zur Primfaktorzerlegung

+ +

+Ohne Hintergrundverarbeitung könnte PrimeNumberTools +instanziert und ihre Methoden durch einfachen Aufruf verwendet werden: + + +

+ + + + + +
+ +
+001 /* Listing2206.java */
+002 
+003 import java.io.*;
+004 
+005 public class Listing2206
+006 {
+007   public static void main(String[] args)
+008   {
+009     PrimeNumberTools pt = new PrimeNumberTools();
+010     BufferedReader   in = new BufferedReader(
+011                           new InputStreamReader(
+012                           new DataInputStream(System.in)));
+013     int num;
+014 
+015     try {
+016       while (true) {
+017         System.out.print("Bitte eine Zahl eingeben: ");
+018         System.out.flush();
+019         num = (new Integer(in.readLine())).intValue();
+020         if (num == -1) {
+021           break;
+022         }
+023         pt.printPrimeFactors(num);
+024       }
+025     } catch (IOException e) {
+026       //nichts
+027     }
+028   }
+029 }
+
+
+Listing2206.java
+ +Listing 22.6: Verwendung der Klasse zur Primfaktorzerlegung

+ +

+Das Programm erzeugt eine Instanz der Klasse PrimeNumberTools +und führt für jeden eingelesenen Zahlenwert durch Aufruf +der Methode printPrimeFactors +die Primfaktorzerlegung durch. Um nun diese Berechnungen asynchron +durchzuführen, entwerfen wir eine Wrapper-Klasse, die von PrimeNumberTools +abgeleitet wird und das Interface Runnable +implementiert: + + +

+ + + + + +
+ +
+001 /* ThreadedPrimeNumberTools.java */
+002 
+003 public class ThreadedPrimeNumberTools
+004 extends PrimeNumberTools
+005 implements Runnable
+006 {
+007   private int arg;
+008   private int func;
+009 
+010   public void printPrimeFactors(int num)
+011   {
+012     execAsynchron(1,num);
+013   }
+014 
+015   public void printPrime(int cnt)
+016   {
+017     execAsynchron(2,cnt);
+018   }
+019 
+020   public void run()
+021   {
+022     if (func == 1) {
+023       super.printPrimeFactors(arg);
+024     } else if (func == 2) {
+025       int result = super.getPrime(arg);
+026       System.out.println("prime number #"+arg+" is: "+result);
+027     }
+028   }
+029 
+030   private void execAsynchron(int func, int arg)
+031   {
+032     Thread t = new Thread(this);
+033     this.func = func;
+034     this.arg  = arg;
+035     t.start();
+036   }
+037 }
+
+
+ThreadedPrimeNumberTools.java
+ +Listing 22.7: Primfaktorzerlegung mit Threads

+ +

+Hier wurde die Methode printPrimeFactors +überlagert, um den Aufruf der Superklasse asynchron ausführen +zu können. Dazu wird in execAsynchron +ein neuer Thread generiert, dem im Konstruktor das aktuelle Objekt +übergeben wird. Durch Aufruf der Methode start +wird der Thread gestartet und die run-Methode +des aktuellen Objekts aufgerufen. Diese führt die gewünschten +Aufrufe der Superklasse aus und schreibt die Ergebnisse auf den Bildschirm. +So ist es möglich, bereits während der Berechnung der Primfaktoren +einer Zahl eine neue Eingabe zu erledigen und eine neue Primfaktorberechnung +zu beginnen. + +

+Um dies zu erreichen, ist in der Klasse Listing2206 +lediglich die Deklaration des Objekts vom Typ PrimeNumberTools +durch eine Deklaration vom Typ der daraus abgeleiteten Klasse ThreadedPrimeNumberTools +zu ersetzen: + + +

+ + + + + +
+ +
+001 /* Listing2208.java */
+002 
+003 import java.io.*;
+004 
+005 public class Listing2208
+006 {
+007   public static void main(String[] args)
+008   {
+009     ThreadedPrimeNumberTools pt;
+010     BufferedReader in = new BufferedReader(
+011                         new InputStreamReader(
+012                         new DataInputStream(System.in)));
+013     int num;
+014 
+015     try {
+016       while (true) {
+017         System.out.print("Bitte eine Zahl eingeben: ");
+018         System.out.flush();
+019         num = (new Integer(in.readLine())).intValue();
+020         if (num == -1) {
+021            break;
+022         }
+023         pt = new ThreadedPrimeNumberTools();
+024         pt.printPrimeFactors(num);
+025       }
+026     } catch (IOException e) {
+027       //nichts
+028     }
+029   }
+030 }
+
+
+Listing2208.java
+ +Listing 22.8: Verwendung der Klasse zur Primfaktorzerlegung mit Threads

+ +

+Wenn alle Eingaben erfolgen, bevor das erste Ergebnis ausgegeben wird, +könnte eine Beispielsitzung etwa so aussehen (Benutzereingaben +sind fett gedruckt): + +

+Bitte eine Zahl eingeben: 991
+Bitte eine Zahl eingeben: 577
+Bitte eine Zahl eingeben: 677
+Bitte eine Zahl eingeben: -1
+primeFactors(577)= 577
+primeFactors(677)= 677
+primeFactors(991)= 991
+
+ +

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

+Obwohl das gewünschte Verhalten (nämlich die asynchrone +Ausführung einer zeitaufwändigen Berechnung im Hintergrund) +realisiert wird, ist dieses Beispiel nicht beliebig zu verallgemeinern. +Die Ausgabe erfolgt beispielsweise nur dann ohne Unterbrechung durch +Benutzereingaben, wenn alle Eingaben vor der ersten Ausgabe abgeschlossen +sind. Selbst in diesem Fall funktioniert das Programm nicht immer +zuverlässig. Es ist generell problematisch, Hintergrundprozessen +zu erlauben, auf die Standardein- oder -ausgabe zuzugreifen, die ja +vorwiegend vom Vordergrund-Thread verwendet wird. Ein- und Ausgaben +könnten durcheinander geraten und es könnte zu Synchronisationsproblemen +kommen, die die Ausgabe verfälschen. Wir haben nur ausnahmsweise +davon Gebrauch gemacht, um das Prinzip der Hintergrundverarbeitung +an einem einfachen Beispiel darzustellen.

+ + + + +
 Hinweis 
+
+ +

+Das nächste Problem ist die Realisierung des Dispatchers in run, +der mit Hilfe der Instanzvariablen func +und arg die erforderlichen Funktionsaufrufe +durchführt. Dies funktioniert hier recht problemlos, weil alle +Methoden dieselbe Parametrisierung haben. Im allgemeinen wäre +hier ein aufwändigerer Übergabemechanismus erforderlich. + +

+Des weiteren sind meistens Vorder- und Hintergrundverarbeitung zu +synchronisieren, weil der Vordergrundprozess die Ergebnisse +des Hintergrundprozesses benötigt. Auch hier haben wir stark +vereinfacht, indem die Ergebnisse einfach direkt nach der Verfügbarkeit +vom Hintergrundprozess auf den Bildschirm geschrieben wurden. Das +Beispiel zeigt jedoch, wie prinzipiell vorgegangen werden könnte, +und ist vorwiegend als Anregung für eigene Experimente anzusehen. +


+ + + +
 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