5.3. java-threads - Parallele und verteilte Systeme

Werbung
T HREADS
Vorlesung 15
➜ Java integriert Threads direkt in die Sprache
➜ Motivation für die Benutzung nebenläufiger Threads:
z.B. graphische Anwendungen, wo Benutzerdialog gleichzeitig
mit Berechnungen im Hintergrund stattfindet
Vor- und Nachteile der zweiten Methode:
+ Kann von anderen Klassen erben
➜ Da sich die thread-safety oft verlangsamend auswirkt, wird
auf sie manchmal verzichtet, wie z.B. in der Swing-Bibliothek
(näheres dazu in der Übung)
VON
– Kann Instanzmethoden von Thread nicht direkt benutzen.
Umweg: über die Klassenmethode currentThread() erhält
man die Referenz auf den aktuellen Thread in der run-Methode
K LASSE T H R E A D
T HREADS
Wie auch andere Konzepte in Java, werden Threads
über Klassen bzw. Vererbung zur Verfügung gestellt
Erste Methode der Thread-Deklaration
in einem Java Programm:
Slide 2
➜ Die Java-Klasse Thread beinhaltet eine
(ursprünglich leere) Methode run()
➜ Die Benutzer-Klasse MyThread wird
als Unterklasse von Thread deklariert
➜ Die (vererbte) Methode run() wird
in MyThread überschrieben
class MyThread extends Thread {
public void run() { ... } }
... Thread a = new MyThread();
c
2007
BY
➜ Die Benutzer-Klasse MyRunnable implementiert Runnable,
indem sie die Methode run() definiert
Merke: Java-Klasse Thread implementiert Runnable ebenfalls
➜ Thread-Safety: für viele Klassen gibt es thread-safe Versionen,
d.h. man kann alle ihre Methoden in nebenläufigen Threads
ohne Zusatzsynchronisation verwenden
JAVA : E RBEN
S ERGEI G ORLATCH · U NI M ÜNSTER · PARALLELE S YSTEME · VORLESUNG 15
RU N N A B L E
➜ Java-Interface Runnable enthält abstrakte Methode run()
Slide 3
➜ Java stellt spezielle Hilfsmittel für Synchronisation zur Verfügung
IN
VON
Zweite Methode der Thread-Deklaration in Java Programmen:
➜ Die Gesamtimplementierung besteht aus 135 Methoden
in vier Klassen und einem Interface
T HREADS
JAVA : I MPLEMENTIEREN
Nachteil der ersten Methode: Abgeleitete Klasse
(wie MyThread ) darf von keiner anderen Klasse
erben, da Java die Mehrfachvererbung verbietet
5.3. JAVA -T HREADS
Slide 1
IN
Thread
run(){}
Slide 4
MyThread
run(){...}
IN
JAVA : E RZEUGEN
UND
S TAR TEN
Ein Thread ist immer ein Objekt der Klasse Thread und wird
(wie jedes Java-Objekt) mit new von einem Konstruktor erzeugt:
➜ Bei der ersten Methode:
Thread a = new MyThread();
➜ Bei der zweiten:
Thread a = new Thread(new MyRunnable());
➜ In beiden Fällen kann der Konstruktor ein String-Argument
haben (der Name des Threads, wird zum Debuggen benutzt);
sonst bekommen die Threads ihre Namen vom System in der
Erzeugungsreihenfolge: Thread-1, Thread-2, ...
➜ Beachte: Ein erzeugter Thread existiert zwar, läuft aber nicht !
Thread muß in beiden Fällen explizit gestartet werden:
a.start();
was die Ausführung seiner run() Methode veranlaßt.
Klassendiagramm
in UML-Notation
(Unified Modeling
Wichtig: Die run Methode läuft parallel (nebenläufig) zu dem
Thread (evtl. main), der start() aufgerufen hat.
Language)
1
c
2007
BY
S ERGEI G ORLATCH · U NI M ÜNSTER · PARALLELE S YSTEME · VORLESUNG 15
2
I MPLEMENTIERUNG
Slide 5
VON C O N
S1; ...; S N ;
END
class Ri implements Runnable {
public void run() { Si; }}
Thread t1 = new Thread(new R1());
...
Thread tn = new Thread(new Rn());
t1.start(); ...; tn.start();
t1.join(); ...; tn.join();
I MPLEMENTIERUNG
Slide 7
VON A T O M I C
S1; ...; S N
END
Object lock = new Object();
...
synchronized (lock) { S1; ...; SN; }
Vorgehensweise:
➜ Wie schon bei Pthreads, ist es die Aufgabe des Programmierers,
jeden potentiellen Gegenpart zu identifizieren und mithilfe von
synchronized auszuschließen.
➜ Jede Klasse, deren Instanzen als Threads laufen sollen, wird als
implements Runnable deklariert
➜ Es wird ein neues Thread-Objekt erzeugt:
An den Konstruktor wird das “Runnable” Objekt übergeben, das
parallel ausgeführt werden soll
➜ Die Methode start des neuen Thread-Objekts wird aufgerufen
➜ Das Thread-Objekt startet die run-Methode
K RITISCHE B EREICHE
MIT S Y N C H R O N I Z E D
M OTIVIERENDES B EISPIEL : Bounded Buffer
➜ Übliche Idee: andere Threads vor dem kritischen Bereich
(Zugriff auf gemeinsame Variablen) anhalten
➜ Arten von synchronized (Anweisungsblock oder Methode):
Beispiel Bounded Buffer: Ein FIFO-Puffer begrenzter Kapazität wird
von einem Producer und einem Consumer nebenläufig benutzt:
➜ Producer schreibt und Consumer liest, unabhängig voneinander
• synchronized (object) { ... Anweisungsblock ... }
Jede Objektinstanz kann als ein Lock benutzt werden
Slide 6
➜ Producer kann nicht schreiben wenn der Puffer voll ist,
➜ Consumer kann nicht lesen, wenn der Puffer leer ist.
• synchronized type methodName(...) {... Rumpf ...}
ist äquivalent zu synchronisiertem Anweisungsblock:
Slide 8
type methodName(...) {
synchronized(this) { ... Rumpf ... }
}
➜ Die zweite Variante ist die übliche:
• Pro Objekt wird immer nur eine synchronized Methode
ausgeführt: Der Thread muß dafür das Objekt-Lock erlangen
• Alle relevanten Methoden unbedingt synchronisieren: Die
nicht-synchronisierten können frei auf Instanzdaten zugreifen!
c
2007
BY
S ERGEI G ORLATCH · U NI M ÜNSTER · PARALLELE S YSTEME · VORLESUNG 15
3
c
2007
BY
S ERGEI G ORLATCH · U NI M ÜNSTER · PARALLELE S YSTEME · VORLESUNG 15
4
M OTIVATION
F ÜR BEDINGTE
E INSATZ
S YNCHRONISATION
• Aktive (initiieren Aktionen): werden als Threads
implementiert, z.B. Producer, Consumer
• Passive (reagieren auf Aktionen): werden als Monitore
(geschützte Objekte) implementiert, z.B. Buffer
➜ Gegenseitiger Ausschluß nötig, aber:
• Consumer braucht keinen Zugriff/Lock, wenn Puffer leer
• Producer braucht keinen Zugriff/Lock, wenn Puffer voll
➜ Implementierungsschema:
• In beiden Fällen: wird die Bedingung nicht erfüllt, muß das
Lock freigegeben werden, sonst blockiert das Programm
Slide 11
➜ Lösung: Thread wartet (ohne Lock) auf eine Bedingung, die von
einem anderen Thread geändert wird; erst dann versucht der
aufgeweckte Thread, den Zugriff/Lock erneut zu erlangen
IN
➜ Beachte: Da wait eine Ausnahme auslösen kann, muß es
immer in einem try-Block aufgerufen werden (hier
vernachlässigt)
JAVA -M ONITOR : A NSCHAULICH
JAVA
➜ Ausführung von wait() im Thread B : den Monitor verlassen,
d.h. Lock abgeben und auf notify oder notifyAll warten
➜ Monitor: Abstraktes Sprachkonstrukt für nebenläufiges
Programmieren (Ursprung ca. 1972 – C. Hoare, P. Brinch-Hansen)
➜ Ausführung von notify bzw. notifyAll im Thread A : einen
bzw. alle wartenden Threads aufwecken (wobei nur ein Thread
in den Monitor eintreten darf)
➜ Ein Monitor kapselt Daten, die von (synchronisierten) Prozeduren
nur im gegenseitigen Ausschluß modifizierbar sind
➜ Zusätzlich: bedingte Synchronisation (Warten auf Bedingung
bzw. Signalisieren der evtl. Änderung der Bedingung)
Slide 10
public synchronized void action(){
while (!B) wait();
// modify data
notifyAll();}
➜ Beachte: while-Schleife nötig, um die Bedingung nach dem
Aufwecken neu zu überprüfen
Eine abstrakte Datenstruktur mit gegenseitigem Ausschluß
und bedingter Synchronisation wird allgemein als Monitor
bezeichnet; Monitore werden in Java (teilweise) unterstützt
M ONITORE
Monitor
➜ In Java:
Slide 12
➜ Prinzip: Ein Monitor wird in Java als eine Klasse mit ausschließlich
synchronized Methoden und geeigneten wait-, notify- und
notifyAll-Anweisungen realisiert
S ERGEI G ORLATCH · U NI M ÜNSTER · PARALLELE S YSTEME · VORLESUNG 15
wait()
notify()
• Bedingte Synchronisation mit: wait zum Warten/Schlafen,
und notify,notifyAll zum Aufwecken
BY
000000000
111111111
111111111
000000000
000000000
111111111
000000000
111111111
000000000
111111111
Data
000000000
111111111
000000000
111111111
000000000
111111111
000000000
111111111
Thread A
• Mit synchronized-Methoden kann der Zugriff auf die
Variablen eines Objekts mutually exclusive gemacht werden
c
2007
M ONITOREN : H ERANGEHENSWEISE
➜ In Anwendungen gibt es i.d.R. zwei Arten von Prozessen:
➜ Producer und Comsumer im BoundedBuffer verwenden
gemeinsame Variablen: Pufferzellen, Elementzähler
Slide 9
VON
...
..
Thread B
➜ Die drei Methoden stehen in class Object zur Verfügung
➜ Sie können nur von einem Thread ausgeführt werden, der
bereits das Lock besitzt, d.h. in einer synchronized Methode ist
5
c
2007
BY
S ERGEI G ORLATCH · U NI M ÜNSTER · PARALLELE S YSTEME · VORLESUNG 15
6
M ONITOR -S EMANTIK
IN
B OUNDED B UFFER
JAVA
➜ Es gibt eine Warteschlange pro Monitorobjekt/Lock
(oder Klasse im Falle von static synchronized)
zum Warten auf (evtl. unterschiedliche) Bedingungen
➜ Bei notify (bzw. notifyAll) passiert folgendes:
Slide 13
• Ein Thread (bzw. alle) wird aus der Schlange ausgewählt.
Auswahl ist beliebig (!): Nicht unbedingt der am längsten
Wartende Thread, der mit der höchsten Priorität, etc.
• Dem Thread einen Prozessor zuweisen (nicht unbedingt
gleich – der aufweckende Thread kann weiter laufen!)
Slide 15
➜ Die wait-Bedingung muß nach Aufwachen neu überprüft
werden, weil:
• nach notifyAll: evtl. mehrere Threads aufgeweckt, der
erste könnte die Bedingung bereits verändert haben
• nach notify: kann unterbrochen werden (wait ist nicht
atomar!), ein neuer Thread kommt evtl. hinzu, erlangt den
Monitor und ändert die Bedingung
P RODUCER
➜ Producer schreibt und Consumer liest, unabhängig voneinander
➜ Producer kann nicht schreiben wenn der Puffer voll ist,
➜ Consumer kann nicht lesen, wenn der Puffer leer ist.
Slide 16
Slide 14
JAVA
class Buffer {
private Object[] buf, int in = 0, int out = 0,
int count = 0, int size = 0;
Buffer(int size) {this.size=size; buf = new Object[size];}
public synchronized void put (Object o) {
while (count==size) wait();
buf[in] = o; in=(in+1)%size;
++count;
notifyAll();}
public synchronized Object get () {
while (count==0) wait();
Object o=buf[out]; buf[out]=null;
out=(out+1)%size; --count;
notifyAll(); return(o);}
}
B EISPIEL : Bounded Buffer
Beispiel Bounded Buffer: Ein FIFO-Puffer begrenzter Kapazität wird
von einem Producer und einem Consumer nebenläufig benutzt:
IN
UND
C ONSUMER
IN
JAVA
class Producer implements Runnable {
...
public void run() {
while(true) {
ThreadPanel.rotate(12);
buf.put(new Character(alphabet.charAt(ai)));
ThreadPanel.rotate(348);}}}
class Consumer implements Runnable {
...
public void run() {
while(true) {
ThreadPanel.rotate(180);
Character c = (Character)buf.get();
ThreadPanel.rotate(180);}}}
c
2007
BY
S ERGEI G ORLATCH · U NI M ÜNSTER · PARALLELE S YSTEME · VORLESUNG 15
7
c
2007
BY
S ERGEI G ORLATCH · U NI M ÜNSTER · PARALLELE S YSTEME · VORLESUNG 15
8
JAVA : P HILOSOPHEN
B EISPIEL : D INING P HILOSOPHERS
Das berühmteste Beispiel für Nebenläufigkeit [E. Dijkstra 1968]
➜ Das Problem: 5 Philosophen
speisen mit 5 Gabeln am
runden Tisch, wobei jeder zum
Essen 2 Gabeln braucht
Slide 17
➜ Ein Philosoph kommt zu einem
zufälligen Zeitpunkt an seinen
Platz, nimmt zwei Gabeln und
ißt beliebig lange
Slide 19
➜ Frage: Wie den Gabelzugriff
regeln, ohne gegenseitiges
Blockieren, Verhungern und
unnötiges Warten?
JAVA : I MPLEMENTIERUNG
MIT T HREADS UND
JAVA : G ABEL
Slide 20
➜ Drei Zustände bei Threads:
• Essen (grün)
• Denken/satt (gelb)
• Warten/hungrig (blau)
BY
S ERGEI G ORLATCH · U NI M ÜNSTER · PARALLELE S YSTEME · VORLESUNG 15
class Philosopher extends Thread {
private int identity; private Fork
public void run() {
while (true) { // applet changes
sleep(...); // thinking
right.get(); // hungry, taking
sleep(...); // turn left
left.get(); // taking left
sleep(...); // eating
right.put(); left.put();}}} //
M ONITOREN
➜ Gabeln sind passiv
⇒ als Monitore modellieren
c
2007
T HREADS
left, right;
are omitted
right
freigeben
Main-Fragment:
for (int i=0;i<N;++i) fork[i]=new Fork(display,i);
for (int i=0;i<N;++i){
phil[i] = new Philosopher(i,fork[(i-1+N)%N],fork[i]);
phil[i].start();}
➜ Philosophen sind aktiv
⇒ als Threads modellieren
Slide 18
ALS
9
ALS
M ONITOR
class Fork {
private boolean taken=false;
private int identity;
Fork(PhilCanvas disp, int id) //Konstruktor
{display=disp; identity=id;}
synchronized void put() {
taken=false; display.setFork(identity,taken);
notify();}
// Gabel frei
synchronized void get() { // Exception m\"oglich!
while (taken) wait();
// Warten bis frei
taken=true;
// Gabel belegt
display.setFork(identity,taken);}
}
c
2007
BY
S ERGEI G ORLATCH · U NI M ÜNSTER · PARALLELE S YSTEME · VORLESUNG 15
10
T HEORIE : N OTWENDIGE
I MPLEMENTIERUNG : D EADLOCK ?
UND
H INREICHENDE B EDINGUNGEN
Vier Bedingungen [Coffman, Elphick and Shoshani 1971]
➀ Serielle Benutzung von Resourcen (gegenseitiger Ausschluß)
Philosophen: Durch Monitore gegeben, ist auch nötig
Slide 23
Slide 21
➁ Inkrementelles Resourcen-Erlangen, d.h. während des Wartens
auf eine Resource gibt der Prozeß die bereits erlangten
Resourcen nicht frei
Philosophen: Die rechte Gabel wird nicht abgegeben, bis die
linke erlangt ist
➂ Keine Preemption, d.h. dem Prozeß kann die bereits erlangte
Resource nicht (von außen) genommen werden
Philosophen: Niemand zwingt zur Abgabe der erlangten Gabel
➃ Wartezyklus existiert, wo jeder Prozeß die Resource hält, auf die
sein Vorgänger wartet
Philosophen: Zyklischer Wartegraph (Wartezyklus) am runden
Tisch, wo jeder die rechte Gabel besitzt und auf die linke wartet
D EADLOCKFREIE L ÖSUNGEN
D EADLOCK
F ÜR
P HILOSOPHEN
➜ Asymetrisches Verhalten: “gerade” Philosophen nehmen
zunächst die linke Gabel, “ungerade” – die rechte
P HILOSOPHEN
• Philosophen sind nicht gleichberechtigt: während 0 mit 1
und 2 mit 3 um die erste Gabeln konkurrieren, hat 4 seine
erste Gabel sofort und ist somit im Vorteil
• Wie konnte das trotz Monitoren passieren?
• Situation: Alle werden gleichzeitig hungrig, jeder nimmt
die rechte Gabel, und versucht die linke zu holen.
Slide 22
F ÜR
Slide 24
• Deadlock: jeder wartet auf die linke Gabel, die nur vom
Nachbar freigegeben werden kann; dies kann aber nicht
passieren, weil der Nachbar auch wartet
⇒ alle warten unendlich lange ...
• Bei einigen Schedulingstrategien kann – obwohl
unwahrscheinlich – Verhungern entstehen
➜ Das Erlangen beider Gabeln atomar machen: wenn frei – essen,
sonst auf einer Bedingungsvariablen warten; gegessen: notify
• + Kein Deadlock, alle gleichberechtigt
• – Verhungern möglich: wenn Philosophen abwechselnd
paarweise essen – 0 mit 2 und 1 mit 3 – wird 4 verhungern
• Fazit: Monitore schützen nicht vor Deadlock!
➜ Extra “Butler”-Prozeß :
nicht mehr als 4 Philosophen gleichzeitig zum Tisch lassen
c
2007
BY
S ERGEI G ORLATCH · U NI M ÜNSTER · PARALLELE S YSTEME · VORLESUNG 15
11
c
2007
BY
S ERGEI G ORLATCH · U NI M ÜNSTER · PARALLELE S YSTEME · VORLESUNG 15
12
S CHEDULING
UND
S TEUERUNG
VON
T HREADS
V ERGLEICH : JAVA -T HREADS
➜ Da es potentiell mehr Threads als CPUs gibt, müssen die Threads
abwechselnd auf Prozessoren arbeiten
➜ Direkter Einfluß auf Scheduling in Java:
Slide 25
POSIX
Thread ( Runnable ) ,
pthread create ()
t . start (), t . join ()
pthread join ()
synchronized ()
pthread mutex init ()
pthread mutex unlock ()
await
Slide 27
pthread cond init ()
obj.wait ()
pthread cond wait (),
obj.notify ()
pthread cond signal ()
obj.notifyAll ()
pthread cond broadcast ()
➜ Man muß bei wait nicht den Mutex angeben, der freigegeben
werden soll wie in Pthreads
➜ In Pthreads darf signal ohne Lock-Erlangen abgeschickt
werden, in Java wäre das für notify ein Fehler
Aktuelles Interleaving kann nicht vorhergesagt werden.
Man muß deshalb beliebiges Interleaving voraussetzen.
VS .
Java
con
pthread mutex lock ()
➜ Scheduling von Threads zur Ausführung ist schwer vorhersagbar,
da Schedulings-Strategien (cooperative, preemptive, etc.)
systemabhängig sind
JAVA -T HREADS
P THREADS
Konzept
atomic
• Priorisierung möglich: von 1 bis 10, default = 5
(plattform-abhängig)
• Thread.yield(); bewirkt einen Thread-Wechsel,
aber nur zu einem gleich priorisierten Thread
(evtl. wird der gleiche Thread ausgewählt).
Macht Sinn nur für non-preemptive Scheduling
VS .
➜ Pthread-Mutexe sind etwas fehleranfälliger
(mgl. vergessene Unlocks)
JAVA -P LATTFORMUNABH ÄNGIGKEIT
➜ Es können keine Annahmen über die Threads-Implementierung
auf der konkreten Maschine gemacht werden
➜ Man muß alle Möglichkeiten erwarten,
aber darf keine von ihnen voraussetzen!
➜ Programmierer muß sein Programm für zwei widersprüchliche
Extremfälle bereit machen:
Slide 26
• Ein Thread kann jederzeit unterbrochen werden, d.h. fast alle
Operationen sind nicht-atomar und müssen in kritischen
Abschnitten mit synchronized abgesichert werden
• Ein Thread kann evtl. beliebig lang nicht unterbrochen
werden, was bei anderen Threads zum Verhungern führen
kann. Lösung: yield oder sleep.
➜ Das ganze Spektrum zwischen den beiden Extremfällen
muß als möglich (aber nicht garantiert!) berücksichtigt werden
c
2007
BY
S ERGEI G ORLATCH · U NI M ÜNSTER · PARALLELE S YSTEME · VORLESUNG 15
13
c
2007
BY
S ERGEI G ORLATCH · U NI M ÜNSTER · PARALLELE S YSTEME · VORLESUNG 15
14
Herunterladen