Java Solutions. Programmier- und Architekturlösungen für die Java-Plattform. Bearbeitet von Markus Kopp, Gerhard Wilhelms

Ähnliche Dokumente
parallele Prozesse auf sequenziellen Prozessoren Ein Process ist ein typisches Programm, mit eigenem Addressraum im Speicher.

Javakurs für Fortgeschrittene

Praktikum aus Softwareentwicklung 2, Stunde 5

Dr. Monika Meiler. Inhalt

OOP: Nebenläufigkeiten Threads. Dipl.-Inform. Arnold Willemer

Lebenszyklus von Threads

Parallele Prozesse Prozeß Parallele Prozesse verzahnte Prozesse Nebenläufige Prozesse: Threads Vorlesung Software-Entwicklung / Folie 131 Ziele:

(b.) Welche Möglichkeit gibt es, die Methode run so zu verändern, dass Bora Bora IMMER zuletzt ausgegeben wird?

Laborskript Verteilte Systeme

Prozesszustände (1a)

Wirtschaftsinformatik II Sommersemester Lo sungshinweise zu den Ü bungen P. Mandl, M. Dolag, B. Rottmüller, et al.

Java Concurrency Utilities

Vorlesung Informatik II

Ausnahmebehandlung in Java

Objektorientierte Programmierung

Die Klasse java.lang.object. Thorsten Treffer

Testen nebenläufiger Objekte

Parallele Prozesse. Prozeß wartet

Nebenläufigkeit mit Java

Liste Programmieren Java Überblick

Nebenläufige Programmierung in Java: Threads

Wintersemester 2009/10 Helmut Seidl Institut für Informatik TU München

Softwaretechnik 1 Übung 5

Programmieren mit Swing

Die Unternehmergesellschaft

Einführung Verteilte Systeme - Java Threads I -

Thread-Synchronisation in in Java. Threads Wechselseitiger Ausschluss Bedingte Synchronisation Beispiel: Warteschlangen

Threading. Arthur Zaczek. Aug 2015

Synchronisation in Java. Invisible Web

Lösungsvorschläge zur Nachklausur zum Kurs 1618 Sommersemester 2001 am

Einführung in die Programmierung Blockkurs Java

JavaScript objektorientiert

Programmieren II. Nebenläufigkeit. Vorlesung 9. Handout S. 1. Dr. Klaus Höppner. Hochschule Darmstadt Sommersemester Threads. Kritische Bereiche

Info B VL 17: Deadlocks

Programmierung mit Threads in Java

Aufgabenblatt 6 Musterlösung

5. Threads, Serverprozesse und Benachrichtigungen

Filme der Kindheit Kindheit im Film

Einführung in die Programmierung

Übung zu Grundlagen der Betriebssysteme. 10. Übung

Institut für Programmierung und Reaktive Systeme. Java 7. Markus Reschke

Klausur Software-Entwicklung September 00

Java I Vorlesung Nebenläufigkeit

Fachhochschule Wedel 31. Januar 2004 Prof. Dr. Uwe Schmidt

Prozesse. Prozesse sind Programme. Prozesse können aus Unterprozessen bestehen. Prozesshierarchie Unterprozesse Threads

Institut fu r Informatik

Aufgabenblatt 8 Musterlösung

Durch die Teil-von-Beziehung soll ausgedrückt werden, dass ein Objekt A als (physikalischer) Teil eines Objekts B angesehen wird. Insbesondere kann ei

Teil 5 - Java. Programmstruktur Operatoren Schlüsselwörter Datentypen

Parallele und verteilte Anwendungen in Java

8.6 Visualisierung der Sortieralgorithmen

Institut fu r Informatik

Philipp Güttler Progwerkstatt Letzte Änderung: Enums, Interfaces, Generics und Threads

import java.applet.applet; import java.awt.*; public class Grow extends Applet { public void start() { setbackground(color.orange); } public void

EINFÜHRUNG IN DIE PROGRAMMIERUNG

Vorlesung Informatik II

Beispiel für überladene Methode

Nebenläufigkeit mit Java

Informatik II. Semesterklausur

Soll die Programm-Ausführung nicht beendet werden, muss der Fehler abgefangen werden. NumberFormatException

Objektorientierte Programmierung OOP Objektorientierte Programmierung (OOP) mit Java

EINFÜHRUNG IN DIE PROGRAMMIERUNG

Vorkurs Informatik WiSe 15/16

Programmieren in Java -Eingangstest-

Grundlagen in C# und.net

Martin Unold INFORMATIK. Geoinformatik und Vermessung

Objektorientierte Programmierung in Java

Übungsblatt 13. Abgabe / Besprechung in Absprache mit dem Tutor

2 Teil 2: Nassi-Schneiderman

Javakurs für Anfänger

Schlussendlich geben wir die Listen aus. Es kommt zu folgender Ausgabe:

Visual C#.NET. Bearbeitet von Patrick A. Lorenz

Verteilte Systeme. 2. Die Client-Server-Beziehung und daraus resultierende Techniken. 2.2 Nebenläufigkeitstechniken in Java

Neben der Verwendung von Klassen ist Vererbung ein wichtiges Merkmal objektorientierter

Ausgewählte Implementierungsprobleme

Objektorientierte Programmierung Studiengang Medieninformatik

Netzwerkprogrammierung unter Linux und UNIX

Einführung in die Programmierung für Nebenfach Medieninformatik. Beat Rossmy, Michael Kirsch

Informatik B. Vorlesung 8 Synchronisierung von Threads. Dr. Ralf Kunze

Bauaufsichtliche Zulassungen - Teil IV: Gewässerschutz (BAZ IV)

Einstieg in die Informatik mit Java

Programmieren 2 Java Überblick

Verkettete Datenstrukturen: Listen

Nebenläufigkeit mit Java

Universität Augsburg, Institut für Informatik Sommersemester 2001 Prof. Dr. Martin Ester 08. Oktober Klausur II

Listing 1: Cowboy. Listing 2: Woody

Probeklausur: Programmierung WS04/05

Prüfung Softwareentwicklung II (IB)

Parallele und funktionale Programmierung Wintersemester 2015/ Übung Abgabe bis , 10:00 Uhr

Einführung in die Programmierung für Nebenfach Medieninformatik. Beat Rossmy, Michael Kirsch

Einstieg in die Informatik mit Java

Einführung in die Informatik 1

ÜBUNGS-BLOCK 8 AUFGABEN

Verteilte Systeme CS5001

Ein erstes "Hello world!" Programm

Physikalisch Technische Lehranstalt Wedel 31. Januar 2004 Prof. Dr. Uwe Schmidt

Programmierung Nachklausurtutorium

Transkript:

Java Solutions Programmier- und Architekturlösungen für die Java-Plattform Bearbeitet von Markus Kopp, Gerhard Wilhelms 1. Auflage 2004. Buch. 400 S. Hardcover ISBN 978 3 446 22550 3 Format (B x L): 17,7 x 24,4 cm Gewicht: 897 g Zu Inhaltsverzeichnis schnell und portofrei erhältlich bei Die Online-Fachbuchhandlung beck-shop.de ist spezialisiert auf Fachbücher, insbesondere Recht, Steuern und Wirtschaft. Im Sortiment finden Sie alle Medien (Bücher, Zeitschriften, CDs, ebooks, etc.) aller Verlage. Ergänzt wird das Programm durch Services wie Neuerscheinungsdienst oder Zusammenstellungen von Büchern zu Sonderpreisen. Der Shop führt mehr als 8 Millionen Produkte.

2 Threads 2.1 Einleitung Mit dem Thread-Konzept steht uns Java-Programmierern eine Möglichkeit zur Verfügung, Programmteile quasi parallel ausführen zu lassen. Quasi parallel bedeutet, dass die Programmteile nicht wirklich parallel ausgeführt werden (außer eventuell bei Multi-Prozessor-Systemen), die Java Virtual Machine schaltet zwischen den einzelnen Ausführungen hin und her, so dass der Eindruck der Parallelität geweckt wird. Anders als bei Prozessen erhalten Threads beim Start keinen eigenen Speicherraum zugewiesen. Alle in einer Applikation gestarteten Threads müssen sich den gemeinsamen Speicher mit der Applikation teilen. Aus diesem Grunde werden Threads auch als leichtgewichtige Prozesse bezeichnet. Die gemeinsame Speichernutzung besitzt den Vorteil, dass die Erzeugung und der Start eines Threads deutlich weniger Systemressourcen und Zeit als der Start eines Prozesses benötigen. Außerdem können Threads sehr einfach miteinander über den gemeinsamen Speicher kommunizieren und benötigen nicht das Konzept des Shared Memory. Die Verwendung von gemeinsamem Speicher besitzt allerdings den Nachteil, dass der Zugriff auf diesen Speicher synchronisiert werden muss. Stellen Sie sich vor, Sie

44 2 Threads schreiben gerade einen Wert in eine (öffentliche) Variable, und bei der nächsten Verwendung besitzt die Variable einen anderen Wert, da ein anderer Thread der Meinung war, ebenfalls in diese Variable schreiben zu müssen. Das dabei entstandende Ergebnis entspricht höchstwahrscheinlich nicht Ihren Vorstellungen ganz zu schweigen von der Schwierigkeit, einen solchen Fehler ausfindig zu machen. Doch bevor Sie jetzt anfangen, alle Speicherzugriffe zu synchronisieren (und damit Ihre Applikation auszubremsen), sollten Sie sich vor der Verwendung von Threads ein paar Gedanken machen. Dann werden Sie schnell feststellen, welche Speicherbereiche, d.h., welche Variablen, Methoden und Klassen synchronisiert werden sollten und welche nicht. In den nachfolgenden Abschnitten werden wir Ihnen zeigen, wie Sie Threads erzeugen, starten, beenden und unterbrechen können, wie Sie Zugriffe synchronisieren und wie Sie kurzlebige Threads vermeiden, indem Sie einen Thread-Pool verwenden. 2.2 Threads richtig anwenden Problem: Wie kann ich einen neuen Thread starten? Lösung: Alle Threads erben von der java.lang.runnable-schnittstelle und müssen somit die run-methode implementieren. Zum Start eines neuen Threads müssen Sie diese Implementierung an ein neues java.lang.thread-objekt übergeben und die start-methode aufrufen. Alles Weitere übernimmt das Thread-Objekt. Dies ist die allgemeine Möglichkeit zum Start eines Threads. Es gibt aber auch noch eine zweite Möglichkeit, indem Ihre Klasse nicht die Runnable-Schnittstelle direkt implementiert, sondern von der Threads-Klasse erbt. Die Threads-Klasse implementiert ihrerseits die Runnable-Schnittstelle, so dass Sie in diesem Fall nur noch die run-methode in Ihrer Klasse überschreiben müssen. Wenn die Klasse, die Sie gerne als Thread hätten, bereits von einer anderen Klasse erbt, bleibt Ihnen nur die Möglichkeit übrig, die Runnable-Schnittstelle zu implementieren und somit die erste Möglichkeit für das Starten eines Threads zu verwenden. Erbt Ihre Klasse noch von keiner anderen (außer von java.lang.object) können Sie die zweite Möglichkeit verwenden, die beim Erzeugen etwas kürzer ist. Problem: Wie kann ich einen Thread beenden?

2.2 Threads richtig anwenden 45 Lösung: Threads werden automatisch beendet, wenn die run-methode verlassen wird. Dies kann entweder durch die korrekte Beendigung dieser Methode oder durch das Auslösen einer Exception erfolgen. Beispiel: Die zwei nachfolgenden Beispiele stellen je ein Template für die beiden besprochenen Möglichkeiten dar, wie ein Thread erzeugt, gestartet und wieder beendet werden kann. Programm 2.1 Thread-Template als Runnable-Schnittstelle public class RunnableTemplate implements Runnable { private boolean isrunning; public void run() { isrunning = true; while( isrunning ) { // hier wird die Arbeit des Threads gemacht catch( Exception e ) { System.err.println("Exception in Thread verursacht" + " das Beenden dieses Threads."); isrunning = false; public void stopme() { isrunning = false; public boolean isrunning() { return isrunning; public static void main(string[] args) { RunnableTemplate rt = new RunnableTemplate(); Thread th = new Thread(rt); th.start(); //... rt.stopme(); Das erste Template zeigt die Implementierung der Runnable-Schnittstelle. Bei diesem Ansatz wird ein neuer Thread erzeugt, indem ein Objekt der Klasse Runnable Template im Konstruktor eines neuen Threads übergeben wird:

46 2 Threads RunnableTemplate rt = new RunnableTemplate(); Thread th = new Thread(rt); Dieser Thread wird durch Aufruf der start-methode gestartet: th.start(); Dieser Aufruf bewirkt, dass die Java Virtual Machine einen neuen Thread erzeugt und in diesem Thread die run-methode des RunnableTemplate-Objekts aufruft. Die run-methode der Klasse RunnableTemplate besteht aus einer Schleife, die so lange durchlaufen wird, bis das Attribut isrunning den Wert false hat. Dies kann entweder durch eine Exception innerhalb der while-schleife passieren oder durch Aufruf der stopme-methode. Dadurch wird die while-schleife und damit auch die run-methode beendet, was zur Folge hat, dass der Thread von der Java Virtual Machine beendet werden kann. Programm 2.2 Ein Thread-Template public class ThreadTemplate extends Thread { private boolean isrunning; public void run() { isrunning = true; while( isrunning ) { // hier wird die Arbeit des Threads gemacht catch( Exception e ) { System.err.println("Exception in Thread verursacht" + " das Beenden dieses Threads."); public void stopme() { isrunning = false; public boolean isrunning() { return isrunning; public static void main(string[] args) { ThreadTemplate tt = new ThreadTemplate(); tt.start(); //... tt.stopme();

2.2 Threads richtig anwenden 47 Das zweite Template zeigt die Erweiterung der Thread-Klasse. Dieses Template ist fast identisch mit dem ersten Template. Lediglich das Erzeugen und Starten eines neuen Threads hat sich von drei Zeilen Code auf zwei vereinfacht: ThreadTemplate tt = new ThreadTemplate(); tt.start(); Wenn Ihr Thread die run-methode durchläuft, sollten Sie darauf achten, dass für andere Threads die Gelegenheit zur Ausführung geschaffen wird. Dies erfolgt in der Regel durch das kurzfristige Schlafenlegen des Threads durch den Aufruf der Thread.sleep-Methode. Diese Methode erhält als Argument die maximale Länge in Millisekunden, die sich Ihr Thread ausruhen darf. Einen schlafenden Thread können Sie jederzeit mit dem Aufruf der interrupt-methode aufwecken. Damit Ihr Thread dies mitbekommt, wird innerhalb von sleep eine InterruptedException ausgelöst: Thread.currentThread().sleep(1000); catch( InterruptedException ie ) { // oh, ich wurde aufgeweckt Problem: Wie kann ich einen Interrupt auslösen? Lösung: Zum Auslösen eines Interrupts müssen Sie lediglich die interrupt-methode des Threads aufrufen, den Sie unterbrechen möchten: public class InterruptExample implements Runnable { public final static long SleepingTime = 1000; private boolean isrunning; private Thread thisthread; public void run() { isrunning = true; thisthread = Thread.currentThread(); while( isrunning ) { System.out.println("Ich bin beim " + "Arbeiten..."); Thread.currentThread().sleep(SleepingTime);

48 2 Threads catch( InterruptedException ie ) { System.err.println("Ich bin unterbrochen " + "worden..."); public void stopme() { isrunning = false; thisthread.interrupt(); public static void main(string[] args) { InterruptExample ie = new InterruptExample(); Thread th = new Thread(ie); th.start(); ThreadHelper.sleep(1200); ie.stopme(); In diesem Beispiel wird der Thread in der stopme-methode durch Aufruf von this Thread.interrupt() unterbrochen und auch beendet, da die Variable isrunning vor der Unterbrechung auf false gesetzt wurde. Der Nachteil dieser Art, einen Thread zu unterbrechen, ist, dass der Thread keine Möglichkeit hat, seine Arbeit noch zu beenden. Wir werden später eine elegantere Möglichkeit zum Beenden eines Threads kennen lernen, ohne dass dieser abgebrochen wird. Problem: Was soll ich machen, wenn eine InterruptedException ausgelöst wurde? Lösung: Sie sollten überprüfen, was es für einen Grund geben könnte, dass Ihr Thread aufgeweckt wurde. In der Regel stehen nun Daten bereit, auf die Sie gewartet haben, oder jemand möchte, dass Ihr Thread sich nun beendet. Problem: Wie kann ich meinen Thread sonst noch benachrichtigen? Lösung: Haben Sie einen Arbeits-Thread, der, sobald neue Daten vorhanden sind, mit der Arbeit beginnen soll, dann können Sie ihn immer wieder schlafen legen und ihn anschließend wieder mit einem interrupt aufwecken. Da hier eine Exception ausgelöst werden muss, ist dies nicht unbedingt die performanteste Lösung. Deshalb bietet Java uns noch eine andere Möglichkeit.

2.2 Threads richtig anwenden 49 Mit den Methoden wait und notify existiert eine elegante Lösung zum Synchronisieren von Threads. Beide Methoden werden in Object definiert und stehen somit jedem Objekt zur Verfügung. Diese Methoden basieren auf dem Monitor-Konzept: Betritt ein Thread einen synchronisierten Bereich, so steht dieser Bereich keinem anderen Thread mehr zur Verfügung. Ruft der erste Thread die wait-methode auf, so wird der Monitor freigegeben, und ein anderer Thread kann in den Bereich. Der erste Thread wartet nun so lange, bis er mit notify wieder aktiviert wird. In diesem Fall kann er gleich weiter arbeiten, da er von dem anderen Thread den Monitor mit dem Aufruf von notify erhalten hat. Um den oben beschriebenen Vorgang zu vereinfachen, verwenden wir die Hilfsklasse aus dem Programm 2.2, welche die beiden Methoden simplywait und simply- Notify zum einfachen Synchronisieren zweier Threads bereitstellt. Programm 2.3 Die Synchronizer-Klasse public class Synchronizer { public void simplywait() { simplywait(-1); public synchronized void simplywait(long time) { if( time > 0 ) { wait(time); else { wait(); catch( InterruptedException e ) { public synchronized void simplynotify() { notifyall(); Beispiel: Das nachfolgende Beispiel zeigt Ihnen, wie Sie wait und notify bzw. simply- Wait und simplynotify zum Synchronisieren von Threads einsetzen können. Das komplette Beispiel können Sie auf der beiliegenden CD finden.

50 2 Threads Programm 2.4 Beispiel für wait/notify public class ThreadExample implements Runnable { public final static long SleepingTime = 1000; private boolean isrunning; private Worker worker; private Object[] data= null; Synchronizer sync = new Synchronizer(); private boolean newdatareceived() { // hier wird überprüft, ob neue Daten zum Bearbeiten // durch den "Worker-Thread" vorhanden sind // hier: einfach per Zufall entscheiden // data == null: alte Daten wurden bereits abgeholt if( data == null && Math.random() > 0.6 ) { // Erzeuge neue Zufallsdaten data = new Object[(int)(Math.random() * 10) +1]; for( int i=0; i<data.length; i++ ) { data[i] = new Integer((int)(Math.random() * 27)); return data!= null; public Object[] getdata() { Object[] tmp = data; // löschen Daten: data = null; return tmp; public void run() { isrunning = true; worker = new Worker(this); Thread workerthread = new Thread(worker); workerthread.start(); while( isrunning ) { if( newdatareceived() ) sync.simplynotify(); ThreadHelper.sleep(SleepingTime); catch( Exception e ) { System.err.println("Exception in Thread verursacht" + " das Beenden dieses Threads."); isrunning = false;

2.2 Threads richtig anwenden 51 public void stopme() { isrunning = false; if( worker!= null ) { worker.stopme(); // benachrichtige den Worker, dass er nun // Feierabend hat - sonst wartet er ewig (1) sync.simplynotify(); worker = null; class Worker implements Runnable { private boolean isrunning; private ThreadExample myboss; Worker(ThreadExample boss) { myboss = boss; public void run() { isrunning = true; while( isrunning ) { (3) dothework(); // warte, bis es wieder etwas zu arbeiten gibt (2) myboss.sync.simplywait(); catch( Exception e ) { System.err.println("Exception in Thread verursacht" + " das Beenden dieses Threads."); isrunning = false;... private void dothework() {... Das obige Beispiel besteht aus zwei Threads, einem Arbeits-Thread (Klasse Worker) und einem zweiten, der die Arbeit organisiert ( Boss ), die der Arbeits-Thread zu erledigen hat. Damit die Vergabe der Arbeit und deren Erledigung effizient gestaltet ist, müssen die zwei Threads sich synchronisieren. In diesem Beispiel verwenden beide Threads ein gemeinsames Synchronizer-Objekt. Liegt eine neue Aufgabe (z. B. neue Daten) an, so ruft der Boss die simplynotify-methode des Synchronizer-

52 2 Threads Objekts auf (1). Der Arbeits-Thread hat zuvor die simplywait-methode (2) des Synchronizer-Objekts aufgerufen, um hierüber benachrichtigt zu werden. Nach Beendigung der simplywait-methode überprüft der Arbeits-Thread, ob neue Aufgaben (Daten) für ihn vorliegen oder ob er benachrichtigt wurde, dass er sich beenden soll. Im ersten Fall waren die Daten durch Aufruf der dothework-methode (3) bearbeitet. Anschließend warted der Arbeits-Thread wieder durch Aufruf der simplywait-methode auf weitere Aufgaben/Benachrichtigungen vom Boss. Der Boss muss allerdings aufpassen, dass er den Arbeiter nur dann benachrichtigt, wenn dieser auch darauf wartet. Ist dieser nämlich mit seiner Arbeit beschäftigt, so ignoriert dieser jede Benachrichtigung für weitere Aufgaben. Mit diesem Wissen über das Synchronisieren von Threads können die obigen Thread- Templates verbessert werden: Stellen Sie sich vor, Sie haben einen Thread, der alle zehn Minuten überprüft, ob und mit welcher Antwortzeit ein HTTP-Server erreichbar ist. Die gesammelten Ergebnisse werden in eine Protokolldatei geschrieben. Beenden Sie diesen Thread durch Aufruf der ersten Version der stopme-methode, so müssen Sie unter Umständen bis zu zehn Minuten warten, bis der Thread merkt, dass er beendet werden soll, und er die offene Protokolldatei schließt. Erst dann kann diese Datei für andere Zwecke verwendet werden. Verwenden Sie hingegen ein interrupt zum Beenden des Threads, dann kann es passieren, dass er während des Schreibens in die Protokolldatei unterbrochen wird und diese in einem nicht definierten Zustand hinterlässt. Beide Varianten sind somit nicht praxistauglich und genau hier kommt unsere obige Synchronizer-Klasse wieder ins Spiel. Anstatt die sleep-methode des aktuellen Threads aufzurufen, wird, wie im obigen Beispiel gezeigt, die simplywait- Methode des Synchronizer-Objekts aufgerufen. Soll dieser Thread früher wieder geweckt werden, so wird die simplynotify-methode des Synchronizer- Objeks aufgerufen. Dieser bemerkt, dass er sich beenden soll, und schließt vorher noch die Protokolldatei, die Sie somit (fast) gleich verwenden können 1. Problem: Ich möchte immer wiederkehrende Aufgaben in Threads auslagern wie kann ich dies erledigen? Lösung: Dazu haben wir uns aus der Unix-Welt inspirieren lassen und einen CronManager programmiert. Dieser Klasse können Jobs, die die Schnittstelle CronJob implementieren, übergeben werden. Diese Jobs werden anschließend in frei wählbaren Intervallen in einem eigenen Thread ausgeführt. 1 Hier empfiehlt es sich, etwas zu warten, damit der Thread Zeit hat, die Datei zu schließen.

2.2 Threads richtig anwenden 53 Alle zur Verfügung stehenden Methoden des CronManager sind statisch. Somit steht immer nur ein CronManager pro Applikation zur Verfügung. Die nachfolgenden Tabellen beschreiben die Methoden der Schnittstelle CronJob und der Klassen CronManager. Beide Klassen befinden sich auf der beiliegenden CD in dem Package threads. Methode int getnotifyinterval() void cron() Beschreibung Liefert die Zeit (in Sekunden), wann dieser Job das nächste Mal aufgerufen werden möchte. Diese Methode wird alle getnotify Interval() Sekunden von dem Cron Manager-Thread aufgerufen. Hier können Sie Ihren Job ausführen. Tabelle 2.1: Methoden der Klasse CronJob Methode static void addcronjob(cronjob cj) static void remove- CronJob(CronJob cj) static boolean iselement(cronjob cj) Beschreibung Fügt einen neuen CronJob hinzu. Löscht den angegebenen CronJob. Liefert true, falls der angegebene CronJob im CronManager enthalten ist. Tabelle 2.2: Methoden der Klasse CronManager Nachfolgendes Beispiel demonstriert die Verwendung des CronManagers. Dieses Beispiel finden Sie auf der beiliegenden CD in dem Verzeichnis src/threads. public class CronExample implements CronJob { public int getnotifyinterval() { // benachrichtige mich einmal/sekunde return 1; public void cron() {

54 2 Threads System.out.println("cron wurde aufgerufen."); public static void main(string[] args) { CronExample ex = new CronExample(); CronManager.addCronJob(ex); int read = System.in.read(); catch( java.io.ioexception ioe) { System.err.println("Fehler beim Lesen: " + ioe); CronManager.removeCronJob(ex); System.out.println("Programm wurde beendet."); Die obige Beispielklasse CronExample implementiert die CronJob-Schnittstelle. Diese Schnittstelle definiert die beiden Methoden cron und getnotifyinterval. Beide Klassen befinden sich auf der beiliegenden CD in dem Verzeichnis src/threads. In der main-methode der Beispielklasse wird eine Instanz der Klasse CronExample erzeugt, die anschließend der statischen Methode addcronjob der Cron Manager-Klasse übergeben wird. Diese ruft zuerst die cron-methode in einem eigenen Thread des CronManagers auf. Danach wird das Benachrichtigungsintervall durch Aufruf der Methode getnotifyinterval ermittelt, und nach dem Verstreichen dieses Intervalls beginnen diese zwei Schritte wieder von vorne. Der main-thread wartet zwischenzeitlich auf eine Tastatureingabe, die zur Beendigung führt. Vor dem Ende wird unser Job für fertig erklärt und aus dem Cron Manager entfernt. 2.3 Ein Thread-Pool Problem: Ich benötige immer wieder für kurze Zeit einen Thread. Wie kann ich es vermeiden, ständig neue Thread-Objekte zu erzeugen und zu starten? Lösung: Verwenden Sie einen Thread-Pool. Ein Thread-Pool ist eine Klasse, welche mehrere Threads verwaltet und Ihnen bei Bedarf einen zum Arbeiten gibt. Wurde Ihre Arbeit

2.3 Ein Thread-Pool 55 beendet, so kommt der Thread zurück in den Pool und wartet auf neue Aufgaben. Leider ist ein Thread-Pool nicht Bestandteil des JDK, weshalb wir unseren eigenen programmiert haben. Die nachfolgende Tabelle 2.3 gibt Ihnen einen Überblick über die zur Verfügung stehenden Methoden der Klasse ThreadPool. Ein neues ThreadPool-Objekt wird erzeugt, indem dem Konstruktor ein Name und die maximale Anzahl der Threads übergeben wird. Der Name wird benötigt, um allen Threads dieses Pools einen einheitlichen Namen zu geben. Um den ThreadPoolManager verwenden zu können, müssen Sie einfach Ihr Objekt, welches die Runnable-Schnittstelle implementiert, über die run-methode diesem Thread-Pool hinzufügen: // also statt: ThreadRunner tr = new ThreadRunner(); Thread thread = new Thread(tr); thread.start(); // starten Sie ThreadRunner wie folgt: int numthreads = 4; ThreadPoolManager tpm = new ThreadPoolManager("test", numthreads); ThreadRunner tr = new ThreadRunner(); tpm.run(tr); Die run-methode des ThreadPoolManager überprüft, ob gerade ein Thread frei ist. In diesem Fall ruft der erste freie Thread Ihre run-methode auf. Sind alle Threads des Thread-Pools beschäftigt, so wird Ihr Objekt in eine Warteschlange aufgenommen und bearbeitet, sobald ein freier Thread zur Verfügung steht. Falls nie ein Thread frei wird, so wird Ihre run-methode auch nie aufgerufen. Ist diese Einschränkung für Ihre Anwendung nicht tragbar, so können Sie ja in der run- Methode des ThreadPoolManager die Anzahl der Threads einfach um einen gewissen Faktor erhöhen, falls alle Threads belegt sind. Oder Sie schreiben einen Cron Job, der regelmäßig überprüft, ob wartende Threads innerhalb eines gewissen Intervalls auch wirklich bearbeitet werden. Methode void init(int numthreads) boolean isthreadavailable() Beschreibung Initialisiert den Thread-Pool mit der angegebenen Anzahl von Threads und startet diese auch. Liefert true, falls derzeit ein freier Thread zur Verfügung steht. Fortsetzung nächste Seite...

56 2 Threads Methode boolean isinit() void run(runnable runnable) void stop() static void stopallthreads() Methoden der Klasse ThreadPoolManager (Forts.) Beschreibung Liefert true, falls dieser Thread-Pool korrekt initialisiert wurde. Falls gerade ein freier Thread vorhanden ist, so wird dieser die run-methode von runnable aufrufen. Ansonsten wird runnable so lange zwischengespeichert, bis ein freier Thread zur Verfügung steht. Beendet alle Threads dieses Pools. Der Aufruf isinit() liefert anschließend false als Ergebnis. Beendet die Threads aller Thread-Pools. Tabelle 2.3: Methoden der Klasse ThreadPoolManager Auf der beiliegenden CD ist der komplette Quellcode der Klasse ThreadPool Manager enthalten.