Thread Funktionalität als
Sprachkonstrukte oder externe Bibliotheken
- Java: Threads als Basisklassen und Spracherweiterungen
- Ada: Threads, ``Tasks'' genannt im Sprachumfang
- C, C++, Modula-2, FORTRAN und andere: Programmbibliotheken
- z.B. POSIX Threads Bibliothek ``Pthread''
- in OSF DCE und anderen Systemen
- OS/2 und Win32: eigene Thread-Systeme
- Userlevel-Threads vs. Kernel-Threads
- Java: green Threads, native Threads
- bei Mehrprozessorrechnern nur Kernel-Threads sinnvoll
- Prozeß (siehe Abbildung 1)
hat zunächst ein Hauptthread
- dieser kann eitere Threads erzeugen, etc.
- Zugriff und Modifikation gemeinsamer Ressourcen
(Speicher, Dateien, Netzverbindungen)
- falls alle Aufgaben getan, Terminierung
- Thread Zustände:
- running,
- blocked,
- ready oder
- terminated
Abbildung 1:
Threads innerhalb von Prozessen
|
Abbildung 1a:
Thread Zustände
|
Java-Implementierung von Threads, enthalten
in dem Java-Package java.lang:
- der Klasse Thread und dem Interface Runnable,
- dem Java-Sprachkonstrukt synchronized und
- den Methoden wait(), notify() der Basisklasse
Object.
Erzeugung von Threads:
ein Objekt einer geeigneten Klasse erzeugen,
Aufruf einer Methode dieses Objekts.
Für die Klasse gibt es zwei Möglichkeiten:
- Bildung einer Subklasse der Thread-Klasse
- Bildung einer Klasse, die das Runnable
Interface implementiert.
Subklasse von Thread
- Vorteil: bequem alle Methoden der Thread-Klasse erben verwenden
- Nachteil: keine Mehrfachvererbung in Java
wenn von Applet abgeleitet wird, kann
nicht gleichzeitig von Thread abgeleitet werden.
Implementierung des Runnable-Interface
- verlangt nur Methode public void run()
- keine weiteren Anforderungen
- zweite Methode bevorzugt
Der Thread-Konstruktor und die Thread-Methoden
start() und join()
haben die folgenden Spezifikationen.
public Thread(Runnable target)
public Thread(Runnable target, String name)
public Thread(ThreadGroup group, Runnable target, String name)
public synchronized void start()
public final void join() throws InterruptedException
- ThreadGroup definieren von Gruppen von Threads
- Operationen zur Steuerung der Mitglieder-Threads
- meist reicht die erste Variante Thread(Runnable target)
- mit start()-Methode wird run()-Methode
der Klasse myRunnable gestartet
- mit join()-Methode warten auf
normale Beendigung des Threads
- Terminierung mit stop() ab Java 1.2 nicht mehr
erster Eindruck eines parallelen Java-Programms:
class Action implements Runnable {
int var;
public Action(int v) { var = v; }
public void run() { doSomeWork(var); }
}
- erster Teil: eine Klasse, die Runnable implementiert
- Konstruktor nimmt einen Parameter entgegen
- doSomeWork() wird in der run()-Methode
verwendet
- run()-Methode wird von
start()-Methode gestartet
Thread t1 = new Thread(new Action(1));
Thread t2 = new Thread(new Action(2));
Thread t3 = new Thread(new Action(3));
try {
t1.start(); t2.start(); t3.start();
t1.join(); t2.join(); t3.join();
}
catch (InterruptedException e) { ... }
- zweiter Teil: Erzeugung von drei Threads t1,
t2 und t3
- Threads werden mit einem neuen Objekt Action erzeugt
- der Reihe nach gestartet mit mit ti.start()
- ti.join() wartet auf die Terminierung
- InterruptedException abfangen
In verschiedenen run()-Methoden u.U.
gleichzeitiger Zugriff auf globale Variablen.
- Vorsicht geboten, da sonst falsche oder zufällige Werte enthalten
- Gegenseitiger Ausschluß
( mutual exclusion)
- Abstimmen der Abarbeitungsschritte
( condition synchronization)
Wir können nicht verhindern, daß Schreib- oder
Lese-Operationen auf den globalen Speicher in
nebenläufigen Prozessen stattfinden und sichtbar werden.
- Trick: Thread in der Ausführung anhalten
- Nachteil: alle ``kritischen'' Bereiche in
allen Threads ausfindig machen
- Absichern durch Haltekonstrukt
- keine so große Einschränkung
- da oft in einer Klasse isolierbar / kontrollierbar
- Anhalten durch synchronized-Sprachkonstrukt
Das Java-Sprachkonstrukt synchronized
hat die folgenden Varianten.
synchronized (object) { ... }
synchronized (static object) { ... }
synchronized type methodName(...) { ... }
static synchronized type methodName(...) { ... }
- erste Variante: object einer beliebigen, von Object
abgeleiteten Klasse als Haltepunkt
- Java-Laufzeitsystem stellt sicher, daß immer nur
maximal ein Thread die Statements
{...}
ausführen kann
- einen ``lock'' setzen (es ``verschließen'')
- ``lock'' kann immer nur einmal zu einem gegebenen Zeitpunkt aktiv
- gegenseitiger Ausschluß (``mutual exclusion'')
- ``lock''-Objekte auch als ``mutex'' bezeichnet
- Objekt als static, dann ``lock'' Systemweit nur einmal
- mehrere Objekts haben dann verschiedene
``locks'', die untereinander nicht synchronisiert sind
Die Semantik von
synchronized type methodName(...) { S1; ...; Sn; }
entspricht
type methodName(...) {
synchronized(this) { S1; ...; Sn; }
}
- Methoden einer Klasse, die nicht als synchronized sind,
können frei auf die Objektvariablen zugreifen
- es findet keine Synchronisation statt
- keine echten Monitore, wie sie von Hoare entwickelt wurden
- Nur wenn alle Nicht- private-Methoden synchronized
und nur private-Variablen, wie Monitor
Problem: Initialisierung innerhalb eines parallelen Ablaufs.
Beispiel: Summe von Vektoren.
Zur Verfügung stehen uns die Object-Funktionen
wait() und notify()
mit den folgenden Spezifikationen:
public final void wait() throws InterruptedException
public final void wait(long timeout)
throws InterruptedException
public final void notify()
public final void notifyAll()
wait() nur innerhalb eines
synchronized Abschnitts
- Aufruf von wait() versetzt den aufrufenden Thread
in den Wartezustand (``blocked'')
- und gleichzeitig wird der Lock ( synchronized)
auf diesen Abschnitt freigegeben
- wartet, bis ein anderer Thread notify()-Methode aufruft
- oder bis ein Timeout stattfindet
- dann warten bis er den Lock auf den Abschnitt
wieder erhält, und dann wieder ``ready'' (``runnable'')
- notify() weckt genau einen Thread im wait-Zustand auf.
- Falls mehrere Threads warten, ist nicht vorhersehbar welcher Thread
aufgeweckt wird.
- signal in Pthreads; mindestens ein Thread
- notifyAll weckt alle an diesem Objekt
wartenden Threads auf.
- Varianten mit timeout: `timed wait'
- Bedingung erneut getesten ob Timeout oder notify.
Fall eines beliebigen Booleschen Ausdrucks der erfüllt sein soll.
- Bedingungsvariable für jede semantisch verschiedene Bedingung
- Test, ob sie wahr ist, an alle Stellen verlegt, an denen
die Bedingung wahr geworden sein könnte
- dort wird mit notify (oder notifyAll) der
Eintritt der Bedingung signalisiert
- es kann von mehreren Threads gleichzeitig oder
kurz hintereinander ein notify ausgelöst werden
- daher die Verzögerungsbedingung erneut testen
- denn weiterer Thread könnte sie in der Zwischenzeit schon
wieder ungültig gemacht haben
- Problem falls notify() vor wait()
- dann immer blockiert (sogenanntes ``lost signal'')
- eine Lösung zu diesem Problem stellen Semaphore dar
- wichtigste Java Sprachkonstrukte zur Thread Programmierung
- Thread(new Runnable), start(), join()
- synchronized (.), synchronized methodName
- wait(), notify()
- Anwendungen sind zum Beispiel Semaphore, Barrieren und
das Bounded-Buffer-Problem
- Verwendung von Threads in der Applet-Programmierung
- auch viel in Java Beans und in den neuen Java Swing Klassen
Abbildung C:
UML Semaphore
|
© Universität Mannheim, Rechenzentrum, 2000-2001.
Last modified: Mon Apr 30 09:14:10 MEST 2001