From 33613a85afc4b1481367fbe92a17ee59c240250b Mon Sep 17 00:00:00 2001
From: Sven Eisenhauer
+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:
+
+
+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:
+
+
+
+
+
+
+ Titel
+ Inhalt
+ Suchen
+ Index
+ DOC
+ Handbuch der Java-Programmierung, 5. Auflage
+
+ <<
+ <
+ >
+ >>
+ API
+ Kapitel 22 - Multithreading
+
+
+
+
+
+22.3 Das Interface Runnable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+public abstract void run()
+
+
+
+java.lang.Runnable
+22.3.1 Implementieren von Runnable
+
+
+
+
+
+
+
+Listing 22.4: Implementieren von Runnable
+
+
+
+
+
+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
+
+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. + + + + +
+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 | +
+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 | +
+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 | +
+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 | +
+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. |
+
+
|
+![]() |
+
+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 + |