Threads - Universität Paderborn

Werbung
Universität Paderborn
Prof. Dr. Stefan Böttcher
Kapitel 4:
Nebenläufigkeit, Threads und
Synchronisation
(in Anlehnung an Material von Prof. Dr. Gerd Szwillus)
Ich bedanke mich bei meinen Kollegen
Prof. Dr. Uwe Kastens und Prof. Dr. Gerd Szwillus
für das von ihnen bereitgestellte Material
zu dieser Vorlesung
Grundlagen der Programmierung 2, SS 2008
1
Universität Paderborn
Prof. Dr. Stefan Böttcher
Gliederung und Ziele diese Kapitels
1.Parallele Prozesse und Threads
Æ Grundbegriffe zu Nebenläufigkeit und Threads verstehen
2.Java-Threads
Æ Programmierkonzepte für Threads in Java kennenlernen
3.Synchronisation, gegenseitiger Ausschluss
Æ Grundprobleme der Synchronisation verstehen lernen
Æ das Konzept des Kritischen Abschnitts verstehen
Æ elementare Synchronisationskonzepte anwenden lernen
4.Monitorkonzept und Bedingungssynchronisation
Æ Monitore als das Synchronisationskonzept anwenden lernen
Æ Techniken der Bedingungssynchronisation kennen lernen
Æ Betriebsmittelvergabe durch Bedingungssynchronisation lösen lernen
Grundlagen der Programmierung 2, SS 2008
2
Universität Paderborn
Prof. Dr. Stefan Böttcher
Was sind parallele Prozesse?
Prozess:
ist Æ Ausführung eines sequentiellen Programmstückes
hat Æ einen ihm zugeordneten Speicher (Adressraum).
veränderlich: Speicherinhalt und Programmposition.
ein Speicherbereich kann mehreren Prozessen zugeordnet sein
Parallele Ausführung von Prozessen:
mehrere Prozesse werden gleichzeitig auf mehreren Prozessoren ausgeführt
p1:
p2:
p3:
Zeit
Grundlagen der Programmierung 2, SS 2008
3
Universität Paderborn
Prof. Dr. Stefan Böttcher
Nebenläufige Prozesse und Threads
Verzahnte Ausführung von Prozessen:
mehrere Prozesse werden exklusiv (nicht gleichzeitig) ausgeführt
Æ häufiger Wechsel des ausgeführten Prozesses,
vermittelt die Illusion, dass alle Prozesse gleichzeitig fortschreiten:
p1:
p2:
p3:
Zeit
möglich, wenn sich mehrere Prozesse einen Prozessor teilen müssen.
Nebenläufige Prozesse:
Prozesse, die parallel oder verzahnt ausgeführt werden können.
Threads:
Prozesse, die parallel oder verzahnt in gemeinsamem Speicher ablaufen
Æ Prozessumschaltung ist besonders einfach und schnell (Æ Tafel).
Grundlagen der Programmierung 2, SS 2008
4
Universität Paderborn
Prof. Dr. Stefan Böttcher
Anwendungen nebenläufiger Prozesse
Benutzungsoberflächen:
Ereignisse werden von einem speziellen Systemprozess weitergegeben
Aufwendige Berechnungen nebenläufig Æ Oberfläche nicht blockiert
Simulation realer Abläufe:
z. B. Produktion in einer Fabrik
Animation:
Veranschaulichung von Abläufen, Algorithmen; Spiele (Billiard)
Steuerung von Geräten:
Prozesse im Rechner steuern mehrere gleichzeitig aktive externe Geräte
Leistungssteigerung durch Parallelrechner:
mehrere Prozesse bearbeiten gemeinsam die gleiche Aufgabe,
z. B. paralleles Sortieren großer Datenmengen.
Grundlagen der Programmierung 2, SS 2008
5
Universität Paderborn
Prof. Dr. Stefan Böttcher
Threads in Java
Java-Threads:
Prozesse, die nebenläufig, gemeinsam im Speicher des Programms ablaufen.
Java-Programm p erzeugt neuen Thread t
Æ t läuft nebenläufig zu p und im selben Speicherbereich wie p
Zwei Techniken zur Thread-Erzeugung in Java:
1. Erben von der Thread-Klasse
2. Implementierung des Interface Runnable
Grundlagen der Programmierung 2, SS 2008
6
Universität Paderborn
Prof. Dr. Stefan Böttcher
Threads in Java: 1. Technik (1)
Erben von der Thread-Klasse
a)
Definiere Klasse T als Unterklasse von Thread,
die Methode run() überschreibt
class T extends Thread { ...
public void run () {
//überschreibt die Thread-Methode run()
...
}
//das als Prozess auszuführende Programmstück
T (...) {...}
// Konstruktor
}
b)
lege Objekt t der Klasse T an (es ist auch ein Thread-Objekt):
Thread t = new T (...);
c)
starte Thread t mit Aufruf der geerbten Methode t.start();
t.start(); // der neue Prozess beginnt neben dem hier aktiven zu laufen
Grundlagen der Programmierung 2, SS 2008
7
Universität Paderborn
Prof. Dr. Stefan Böttcher
Threads in Java: 1. Technik (2) Beispiel
Einfaches Beispiel:
class T extends Thread {
private String name;
private int anzahl;
T(String n, int a) { name = n; anzahl = a; } // Konstruktor
public void run() { // wird aufgerufen, wenn Thread gestartet wird
System.out.print("Thread "+name+" fängt an / ");
for (int i=0; i<anzahl; i++)
// der Thread scheibt „anzahl“ mal seinen Namen
System.out.print(name);
System.out.print("Thread "+name+" hört auf / ");
}
}
Grundlagen der Programmierung 2, SS 2008
8
Universität Paderborn
Prof. Dr. Stefan Böttcher
Threads in Java: 1. Technik (3) Beispiel
Beispiel fortgesetzt: die main-Funktion startet 4 Threads,
die jeweils sooft wie angegeben ihren Namen ausgeben.
class T extends Thread { ... }
// wie vorher
// Im Hauptprogramm werden vier T-Objekte direkt als Thread eingesetzt:
class TTest2 {
public static void main (String [] args) {
Thread t1 = new T("1", 10);
Thread t2 = new T("2", 2);
Thread t3 = new T("3", 4);
Thread t4 = new T("4", 1);
t1.start(); t2.start(); t3.start(); t4.start();
}
}
Grundlagen der Programmierung 2, SS 2008
TTest2.java
9
Universität Paderborn
Prof. Dr. Stefan Böttcher
Threads in Java: 2. Technik (1) Überblick
a) Eine Benutzerklasse implementiert das Interface Runnable:
class Aufgabe implements Runnable {
...
public void run () { // vom Interface geforderte Methode run
...// das als Prozess auszuführende Programmstück
}
Aufgabe (...) {...}
// Konstruktor
}
b) Der Prozess wird als Objekt der vordefinierten Klasse Thread erzeugt,
ihm ein Objekt der Benutzerklasse übergeben wird:
Thread auftrag = new Thread (new Aufgabe (...));
c) Erst Aufruf Thread-Methode start startet dann den Prozess:
auftrag.start();
// Der neue Prozess beginnt,
// neben bereits aktiven Prozessen zu laufen.
Grundlagen der Programmierung 2, SS 2008
10
Universität Paderborn
Prof. Dr. Stefan Böttcher
Threads in Java: 2. Technik (2) Beispiel
class T implements Runnable { // so könnte T Unterklasse von anderen
private String name;
// Klassen als Thread sein
private int anzahl;
public void run() {
System.out.print("Thread "+name+" fängt an / ");
for (int i=0; i<anzahl; i++)
System.out.print(name);
System.out.print("Thread "+name+" hört auf / ");
}
T(String n, int a) { name = n; anzahl = a; }
}
TTest1.java
Grundlagen der Programmierung 2, SS 2008
11
Universität Paderborn
Prof. Dr. Stefan Böttcher
Threads in Java: 2. Technik (3) Beispiel
Im Hauptprogramm werden vier Threads aus den „Runnable“-Objekten vom
Typ T mit verschiedenen Parametern erzeugt und gestartet:
class TTest1 {
public static void main (String [] args) {
Thread t1 = new Thread(new T("1", 10));
Thread t2 = new Thread(new T("2", 2));
Thread t3 = new Thread(new T("3", 4));
Thread t4 = new Thread(new T("4", 1));
t1.start(); t2.start(); t3.start(); t4.start();
}
}
TTest1.java
Grundlagen der Programmierung 2, SS 2008
12
Universität Paderborn
Prof. Dr. Stefan Böttcher
Unterschiede beider Techniken
class T implements Runnable {
...
public void run() {
// Prozess
}
...
}
class T extends Thread {
...
public void run() {
// Prozess
}
...
}
T kann Unterklasse einer anderen Klasse
sein
T kann nicht Unterklasse einer anderen
Klasse als Thread sein
Innerhalb der Klasse T stehen die
Methoden von Thread nicht unmittelbar
zur Verfügung
Innerhalb der Klasse T stehen die
Methoden von Thread zur Verfügung
Thread t = new Thread(new T(...));
Thread t = new T(...);
t.start();
t.start();
Grundlagen der Programmierung 2, SS 2008
13
Universität Paderborn
Prof. Dr. Stefan Böttcher
Gemeinsamkeiten beider Techniken
Klasse Thread verwaltet
• die Ausführung der run-Methode
im Zusammenspiel mit allen anderen Prozessen
• Methoden zum Ändern des dynamischen Zustands des Prozesses
• Prioritäten, gegenseitigen Ausschluss, Signalisieren von Bedingungen,
Exceptions
Grundlagen der Programmierung 2, SS 2008
14
Universität Paderborn
Prof. Dr. Stefan Böttcher
Ausführungszustände von Threads
new:
runnable:
waiting:
timed waiting:
terminated:
new
Prozess ist noch nicht gestartet
Prozess läuft
Prozess wartet auf einen anderen Prozess (Bedingung)
Prozess wartet für eine bestimmte Zeit
Prozess ist abgeschlossen
// Programm
// startet Thread
t1.start( ) ;
runnable
Thread
ist fertig
terminated
warten auf B
wait(B) ;
waiting
schlafen legen
sleep(500) ;
timed out
weiter
timed
waiting
Grundlagen der Programmierung 2, SS 2008
15
Universität Paderborn
Prof. Dr. Stefan Böttcher
Wichtige Methoden der Klasse Thread
public void run ()
wird überschrieben mit der Methode,
die die auszuführenden Anweisungen enthält
public void start ()
startet die Ausführung des Prozesses, nur einmal pro Thread möglich!
public void join () throws InterruptedException
der aufrufende Prozess wartet bis der angegebene Prozess terminiert ist:
try { auftrag.join(); } catch (InterruptedException e) { }
public static void sleep (long millisec) throws InterruptedException
aufrufender Prozess wartet mindestens die in Millisekunden angegebene Zeit:
try { Thread.sleep (1000); } catch (InterruptedException e) { }
Grundlagen der Programmierung 2, SS 2008
16
Universität Paderborn
Prof. Dr. Stefan Böttcher
Threads warten auf andere – join-Bsp.(1)
Übergabe eines anderen Threads,
auf den mit dem Abschluss ggf. (!) gewartet wird
class T extends Thread {
private String name; private int anzahl, sleepTime; private Thread wartetAuf;
T( ) { … } // übernimmt name, anzahl, sleepTime, wartetAuf
public void run() {
System.out.println(" Thread "+name+" faengt an / ");
for (int i=0; i<anzahl; i++) {
try { sleep(sleepTime); } catch(InterruptedException ie) { }
System.out.print(name);
}
if (!(wartetAuf == null))
try { wartetAuf.join(); } catch(InterruptedException ie) { }
System.out.println(" Thread "+name+" endet / ");
}
}
Grundlagen der Programmierung 2, SS 2008
17
Universität Paderborn
Prof. Dr. Stefan Böttcher
Threads warten auf andere – join-Bsp.(2)
Übergabe eines anderen Threads,
auf den mit dem Abschluss ggf. (!) gewartet wird
z.B. Starten von vier Threads:
class TTest3 {
public static void main (String [] args) {
Thread t1 = new T("1", 10, 1000, null);
Thread t2 = new T("2", 10, 4000, t1);
Thread t3 = new T("3", 20, 600, t2);
Thread t4 = new T("4", 25, 500, t3);
}
}
// wartet auf niemanden
// wartet auf t1
// wartet auf t2
// wartet auf t3
TTest3.java
Grundlagen der Programmierung 2, SS 2008
18
Universität Paderborn
Prof. Dr. Stefan Böttcher
Synchronisation – Einführung (1)
Wenn mehrere Prozesse die Werte gemeinsamer Variablen verändern,
kann eine ungünstige Verzahnung (Parallelausführung) zu
inkonsistenten Daten führen.
Beispiel (Geburtstag):
- zwei Prozesse p (Tante) und q (Oma) überweisen zum Geburtstag
- benutzen gemeinsame Variable konto und je eine lokale Variable:
Prozess p: tmp = konto; konto = tmp + 300;
Prozess q: tmq = konto; konto = tmq + 1000;
p holt sich Kontostand 400 €, q auch,
q schreibt 1400 €, p überschreibt mit 700 €
1000 € futsch ... !
Was könnte man tun? Was für ein Konzept hätte man gerne
Æ überlegen Sie !
Grundlagen der Programmierung 2, SS 2008
19
Universität Paderborn
Prof. Dr. Stefan Böttcher
Synchronisation – Einführung (2)
Experiment: Ist es wirklich so schlimm?
SynchTest.java
Zwei Prozesse, jeder führt 5 Buchungen durch, laufen nebenläufig
ohne Kontrolle
Jeder Prozess hat eine individuelle Bearbeitungsdauer und Wartezeit
zwischen je zwei Buchungen
class Ablauf extends Thread {
private int tmp;
private int betrag;
private int zeitBedarf, zeitAbstand;
private String name;
Ablauf (String name, int zeitBedarf, int zeitAbstand, int betrag) { ... }
public void run() { … }
}
Grundlagen der Programmierung 2, SS 2008
20
Universität Paderborn
Prof. Dr. Stefan Böttcher
Synchronisation – Einführung (3)
class Ablauf extends Thread { // …
public void run() {
for (int i=0; i<5; i++) {
// 5 Buchungen werden durchgeführt
// Warten auf die nächste Buchung
try { sleep(zeitAbstand); } catch(InterruptedException ie) { };
tmp = konto;
// Durchführung der Buchung
System.out.println(name+" holt sich Kontostand "+tmp);
// sleep simuliert die Dauer der Berechnung
try { sleep(zeitBedarf); } catch( InterruptedException ie ) { } ;
konto = tmp + betrag;
System.out.println(name+" setzt Kontostand auf "+(tmp+betrag));
}
}
}
Grundlagen der Programmierung 2, SS 2008
21
Universität Paderborn
Prof. Dr. Stefan Böttcher
Synchronisation – Einführung (4)
Anlegen zweier Buchungsprozesse A und B:
private Ablauf tante = new Ablauf("Prozess A", 500, 300, 300);
private Ablauf oma = new Ablauf("Prozess B", 200, 400, 1000);
Prozess A holt sich Kontostand 400
Prozess B holt sich Kontostand 400
…
Kontostand am Ende: 2500
A bucht alle 300 ms und braucht dafür 500ms
B bucht alle 400 ms und braucht dafür 200ms
Typisches Ergebnis
Æ unrealistisch? Langsame Ausführung, kurze Abstände...!
Fehler werden seltener, treten aber doch immer wieder auf!
Also: was können wir tun?
Grundlagen der Programmierung 2, SS 2008
22
Universität Paderborn
Prof. Dr. Stefan Böttcher
Kritischer Abschnitt
Kritischer Abschnitt eines Prozesses:
eine oder mehrere zusammengehörige Operationen,
in denen ein Prozess Variablen liest oder ändert,
die von mehreren Prozessen gemeinsam benutzt werden
Synchronisationsregel:
Prozesse müssen kritische Abschnitte unter gegenseitigem Ausschluß
("mutual exclusion") ausführen:
d.h., zu jedem Zeitpunkt darf sich höchstens ein Prozess in seinem
kritischen Abschnitt befinden.
Andere Prozesse, die für die gleichen Variablen mit der Ausführung
eines kritischen Abschnitts beginnen wollen, müssen warten.
Grundlagen der Programmierung 2, SS 2008
23
Universität Paderborn
Prof. Dr. Stefan Böttcher
Monitorkonzept
Monitor :
Ein Modul, der Daten und die Operationen darauf kapselt.
Die kritischen Abschnitte von Operationen auf den Daten
werden als Monitor-Operationen formuliert.
Prozesse rufen Monitor-Operationen auf,
um auf die Daten zuzugreifen.
Monitor-Operationen werden unter gegenseitigem Ausschluss
ausgeführt.
Grundlagen der Programmierung 2, SS 2008
24
Universität Paderborn
Prof. Dr. Stefan Böttcher
Monitore in Java
Anweisungsblöcke oder Methoden einer Klasse, die kritische
Abschnitte auf Objektvariablen implementieren, können als
synchronized gekennzeichnet werden:
class Konto {
synchronized public void kontoBewegung (...) {...}
...
private int stand ;
// kontostand
}
Jedes Objekt der Klasse wirkt als Monitor für seine (!) Objektvariablen:
Aufrufe von synchronized-Methoden von mehreren Prozessen für
dasselbe Objekt werden unter gegenseitigem Ausschluss ausgeführt.
Zu jedem Zeitpunkt kann pro Bank-Objekt (!) höchstens 1 Prozess
mit synchronized Methoden auf Objektvariablen zugreifen.
Grundlagen der Programmierung 2, SS 2008
25
Universität Paderborn
Prof. Dr. Stefan Böttcher
Monitore in Java – Konto-Beispiel (1)
Nochmal unser Experiment (erst ohne Monitor, dann mit Monitor):
class Konto { // Eine Klasse für die Konten – jedes Konto ein Objekt
private int stand = 0;
public void bewegung(String name, int betrag) // Kritischer Abschnitt
{
int tmp;
tmp = stand;
System.out.println(name+": Gelesener Kontostand "+ tmp);
tmp = tmp + betrag;
System.out.println(name+": Geschriebener Kontostand "+ tmp);
stand = tmp;
}
}
Ablauf-Klasse beschreibt Prozess, der fünfmal das Gleiche bucht
Im Hauptprogramm wird ein Konto angelegt und zwei Abläufe werden
darauf losgelassen - Übergabe von Bearbeitungszeit und Wartezeit
OhneMonitorTest.java
Grundlagen der Programmierung 2, SS 2008
26
Universität Paderborn
Prof. Dr. Stefan Böttcher
Monitore in Java – Konto-Beispiel (2)
(mit Monitor):
class Konto { // Eine Klasse für die Konten – jedes Konto ein Objekt
…
// wie vorher
synchronized public void bewegung(String name, int betrag) // Monitor
{
// … wie vorher
}
}
Änderung: Schlüsselwort synchronized bei bewegung(...):
synchronized public void bewegung(String name, int betrag) {...}
Methode kann nun pro Konto-Objekt von maximal einem Prozess
zur Zeit ausgeführt werden!
MonitorTest.java
Korrekter Ablauf, unabhängig von den Zeitangaben!
Grundlagen der Programmierung 2, SS 2008
27
Universität Paderborn
Prof. Dr. Stefan Böttcher
Monitore in Java – Konto-Beispiel (3)
Eigenschaft aller Beispiele zu Monitoren:
Operationen im kritischen Abschnitt
(hier: Zugriff auf Bewegung eines Kontos)
nur im wechselseitigen Ausschluss
Eigenschaft dieses Beispiels:
sobald die "Ressource" Konto frei ist,
kann jedes der beiden Ablauf-Objekte diese nutzen,
es gibt keine weitere Vorbedingung!
Daher reicht die Sicherstellung, dass kein doppelter Zugriff im
kritischen Abschnitt passiert.
Das reicht aber nicht immer Æ folgende Beispiele
Grundlagen der Programmierung 2, SS 2008
28
Universität Paderborn
Prof. Dr. Stefan Böttcher
Bedingungssynchronisation
Bedingungssynchronisation:
Ein Prozess wartet, bis eine Bedingung erfüllt ist –
verursacht durch einen anderen Prozess;
z.B. erst dann in einen Puffer schreiben, wenn darin Platz ist.
Bedingungssynchronisation im Monitor:
Ein Prozess,
der in einer kritischen Monitor-Operation auf eine Bedingung wartet,
muss den Monitor freigeben,
damit andere Prozesse den Zustand des Monitors ändern können.
Grundlagen der Programmierung 2, SS 2008
29
Universität Paderborn
Prof. Dr. Stefan Böttcher
Bedingungssynchronisation in Java
mit vordefinierten Methoden der Klasse Object
(müssen aus synchronized Abschnitten heraus aufgerufen werden)
wait( )
blockiert den aufrufenden Prozess und gibt den Monitor frei –
das Objekt, dessen synchronized Methode er gerade ausführt
notify( )
weckt einen der in diesem Monitor blockierten Prozesse;
welcher ist nicht definiert
notifyAll( )
weckt alle in diesem Monitor blockierten Prozesse;
sie können weiterlaufen, sobald der Monitor frei ist
Grundlagen der Programmierung 2, SS 2008
30
Programmierschema zur
Bedingungssynchronisation in Java
Universität Paderborn
Prof. Dr. Stefan Böttcher
a) Anforderung
Solange meine Anforderung nicht erfüllt: wait();
// Hier geht’s nur weiter, wenn andere mich "benachrichtigen"
Wenn Anforderung jetzt erfüllt: Ressourcen nutzen.
Sonst: wieder warten!
b) Rückgabe
Ressourcen zurückgeben
Allen Wartenden sagen, dass sie es jetzt wieder probieren können:
notifyAll();
Wichtig bei a):
Nachdem ein blockierter Prozess geweckt wurde, muss er die
Bedingung, auf die er wartet, erneut prüfen –
sie könnte schon durch schnellere Prozesse wieder ungültig sein:
while (!erfüllt) try { wait( ); } catch ( InterruptedException e ) { }
Grundlagen der Programmierung 2, SS 2008
31
Beispiel: Betriebsmittelvergabe (1)
Universität Paderborn
Prof. Dr. Stefan Böttcher
Monitor zur Vergabe von Ressourcen („Betriebsmitteln“):
Aufgabe:
- Monitor verwaltet eine begrenzte Anzahl gleichartiger Ressourcen
- Prozesse fordern einige Ressourcen an und geben sie später zurück
Programmstruktur:
Objekt einer Klasse Monitor
Æ verwaltet die verfügbaren Ressourcen (zählen genügt)
Eine Klasse Verbraucher (Unterklasse von Thread),
Æ Verbraucherprozess - mehrfach instanziiert;
alle Verbraucher „streiten“ sich um die Ressourcen
ein Hauptprogramm
Æ legt fest, wie viele Exemplare der Ressource es gibt
Æ startet die Verbraucher.
Grundlagen der Programmierung 2, SS 2008
32
Universität Paderborn
Prof. Dr. Stefan Böttcher
Beispiel: Betriebsmittelvergabe (2)
Der Monitor:
class Monitor { // Anzahl der verfügbaren Ressourcen, diese Variable
private int vorhanden;
// wird überwacht!
Monitor(int v) { vorhanden = v; } // Initialisierung mit der Maximalzahl
synchronized void anfordern(int n, int wer) { ... }
// Mit dieser Anfrage an den Monitor fordert Verbraucher “wer”
// insgesamt n Elemente der Ressource an
synchronized void freigeben(int n, int wer) { ... }
// Mit dieser Anfrage gibt der Verbraucher “wer” die von ihm
// gebrauchten insgesamt n Elemente der Ressourcen zurück
}
Grundlagen der Programmierung 2, SS 2008
33
Universität Paderborn
Prof. Dr. Stefan Böttcher
Beispiel: Betriebsmittelvergabe (2)
class Monitor { …
// Verbraucher Nummer “wer” fordert “n” Ressourcen an
synchronized void anfordern (int n, int wer) {
// solange nicht genug verfügbar sind...
while (vorhanden < n)
{
// wartet der Prozess bis er “geweckt” wird
try { wait(); } catch(InterruptedException e) {}
} // jetzt while-Schleife zuende Æ genug Ressourcen vorhanden!
vorhanden -= n; // Her damit!
}
// Die Rückgabe: Verbraucher “wer” gibt “n” Ressourcen zurück
synchronized void freigeben (int n, int wer) {
vorhanden += n;
// Die eigentliche Rückgabe
notifyAll();
// Alle wartenden Prozesse werden geweckt
}
}
Grundlagen der Programmierung 2, SS 2008
34
Universität Paderborn
Prof. Dr. Stefan Böttcher
Beispiel: Betriebsmittelvergabe (3)
Der Verbraucherprozeß:
class Verbraucher extends Thread {
private Monitor mon; // merkt sich, welcher Monitor ihn bedient
private int ident,
// Identifikation des Prozesses (0, 1, 2, ...)
wieOft,
// Wie oft fordert der Prozess Ressourcen an?
max; // Wie viele Ressourcen werden maximal angefordert?
Verbraucher(...) {...}
// Der übliche Konstruktor
public void run()
{ while (wieOft > 0)
// Iteration über die Anforderungen
{ // Wieviel Ressourcen werden angefordert (zwischen 1 und max)
int anforderung = (int)( Math.random()*(max-1) ) + 1;
mon.anfordern(anforderung, ident); // Die Anforderung
// Verwendung der Ressource für 1...1000 ms (Zufall)
try { sleep( (int)( Math.random()*999 ) + 1); }
catch(InterruptedException e) {}
mon.freigeben(anforderung, ident); // Die Freigabe
wieOft--;
}
}
}
Grundlagen der Programmierung 2, SS 2008
35
Universität Paderborn
Prof. Dr. Stefan Böttcher
Beispiel: Betriebsmittelvergabe (4)
Das Hauptprogramm (MonitorMitRessourcen.java):
class MonitorMitRessourcen {
public static void main(String [] args) {
int vorhanden = 20;
// Maximal sind 20 Ressourcen verfügbar
Monitor mon = new Monitor (vorhanden);
// Starte 5 neue Verbraucher, die je 4 Anforderungen erzeugen
for (int i=0; i < 5; i++)
new Verbraucher(mon, i, 4, vorhanden).start();
// jeder Verbraucher darf maximal 20 ("vorhanden" viele)
// Ressourcen anfordern
}
}
Grundlagen der Programmierung 2, SS 2008
36
Universität Paderborn
Prof. Dr. Stefan Böttcher
Beispiel: Betriebsmittelvergabe (5)
Ein typischer Ablauf:
Verbraucher 0 fordert 15 Elemente an.
Verbraucher 0 bekommt 15 Elemente. Jetzt verfügbar: 5
Verbraucher 1 fordert 5 Elemente an.
Verbraucher 1 bekommt 5 Elemente. Jetzt verfügbar: 0
Verbraucher 2 fordert 19 Elemente an.
Verbraucher 3 fordert 9 Elemente an.
Verbraucher 4 fordert 13 Elemente an.
Verbraucher 1 gibt 5 Elemente frei. Jetzt verfügbar: 5
Verbraucher 1 fordert 17 Elemente an.
Verbraucher 0 gibt 15 Elemente frei. Jetzt verfügbar: 20
Verbraucher 0 fordert 19 Elemente an.
Verbraucher 0 bekommt 19 Elemente. Jetzt verfügbar: 1
Verbraucher 0 gibt 19 Elemente frei. Jetzt verfügbar: 20
Verbraucher 0 fordert 2 Elemente an.
...
Grundlagen der Programmierung 2, SS 2008
37
Universität Paderborn
Prof. Dr. Stefan Böttcher
Betriebsmittelvergabe (6) Zusammenfassung
Schema:
Monitor verwaltet endliche Menge von k ≥ 1 gleichartigen Ressourcen.
Prozesse fordern jeweils unterschiedlich viele (n) Ressourcen an,
1 ≤ n ≤ k, und geben sie nach Gebrauch wieder zurück.
Wartebedingung für Anfordern ist „Sind n Ressourcen verfügbar?“
Zu jedem Zeitpunkt zwischen Aufrufen der Monitor-Methoden gilt:
„Die Summe der freien und der vergebenen Ressourcen ist k.“
Beispiele:
• Walkman-Vergabe an Besuchergruppen im Museum
• auch abstrakte Ressourcen, z. B. das Recht, eine Brücke begrenzter
Kapazität (Anzahl Fahrzeuge oder Gesamtgewicht) zu befahren.
• auch gleichartige Ressourcen mit Identität, z.B.
Nummern der vom Taxi-Unternehmen vergebenen Taxis
(Der Monitor muss dann die Identitäten der Ressourcen verwalten.)
Grundlagen der Programmierung 2, SS 2008
38
Universität Paderborn
Prof. Dr. Stefan Böttcher
Beschränkter Puffer (1) - Problemstellung
Schema: Beschränkter Puffer
Ein Monitor speichert Elemente in einem Puffer von beschränkter Größe.
Produzenten-Prozesse liefern einzelne Elemente,
Konsumenten-Prozesse entnehmen einzelne Elemente.
Der Monitor stellt sicher, dass
die Elemente in der gleichen Reihenfolge geliefert und entnommen werden.
(Datenstruktur: Schlange, Queue)
Die Wartebedingungen lauten:
- in den Puffer schreiben, nur wenn er nicht voll ist,
- aus dem Puffer nehmen, nur wenn er nicht leer ist.
Grundlagen der Programmierung 2, SS 2008
39
Universität Paderborn
Prof. Dr. Stefan Böttcher
Beschränkter Puffer (2) – Struktur der Lösung
Was brauchen wir?
1. Ein Hauptprogramm: startet Produzenten und Verbraucher
2. Eine Klasse (Buffer) für das Monitorobjekt
Æ verwaltet die verfügbaren Ressourcen in einer Warteschlange
3. Eine Klasse Produzent (Unterklasse von Thread) - mehrfach instanziiert;
alle Produzenten wollen ihre „Produkte“ in die Warteschlange schreiben
(Operationen enqueue - einspeichern, dequeue – entfernen)
4. Eine Klasse Verbraucher (Unterklasse von Thread) - mehrfach instanziiert;
alle Verbraucher „streiten“ sich um die in der Schlange verwalteten Produkte
5. (hier zufällig:) eigene Hilfsklassen für Queue und Produkt
Grundlagen der Programmierung 2, SS 2008
40
Beschränkter Puffer (3) – Details der Lösung
das Hauptprogramm
Universität Paderborn
Prof. Dr. Stefan Böttcher
class MonitorMitPuffer {
public static void main(String [] args) {
Buffer puffer = new Buffer(20);
// Lager nimmt max. 20 Autos auf
// Die Produzenten und ihre Leistungsfähigkeit: 10 VWs, 3 BMWs, ...
new Produzent("VW", 10, puffer).start();
new Produzent("BMW", 3, puffer).start();
new Produzent("Mercedes", 2, puffer).start();
new Produzent("Trabant", 18, puffer).start();
// Die Konsumenten und ihre Gier: 3 Autos für Jan, 10 für Tom, ...
new Konsument(„Jan", 3, puffer).start();
new Konsument("Tom", 10, puffer).start();
new Konsument("Gero", 5, puffer).start();
new Konsument("Gunnar", 4, puffer).start();
new Konsument("Gerd", 8, puffer).start();
// Es werden 33 Autos hergestellt und 30 abgenommen
}
}
Grundlagen der Programmierung 2, SS 2008
MonitorMitPuffer.java
41
Beschränkter Puffer (4) – Details der Lösung
Monitorklasse für beschränkten Puffer
class Buffer {
private Queue buf;
Universität Paderborn
Prof. Dr. Stefan Böttcher
// Schlange der Länge n zur Aufnahme der Elemente
public Buffer (int n) { buf = new Queue(n); }
// Die Größe n der Schlange modelliert die Begrenztheit des Puffers
synchronized public void put (Object elem) { ... }
// Methode zum Ablegen eines neu produzierten Elementes
synchronized public Object get () { ... }
// Methode zum Abholen (Verbrauchen) eines Elementes
}
Grundlagen der Programmierung 2, SS 2008
42
Universität Paderborn
Beschränkter Puffer (5) – Details der Lösung Prof. Dr. Stefan Böttcher
Monitorklasse für beschränkten Puffer - Details
private Queue buf;
// Schlange der Länge n zur Aufnahme der Elemente
synchronized public void put (Object elem) {
// ein Produzenten-Prozess versucht, ein Element zu liefern
while (buf.isFull()) {
// warten solange der Puffer voll ist
try { wait(); } catch (InterruptedException e) {}
// Jetzt ist es soweit: Puffer ist nicht mehr voll
buf.enqueue (elem);
notifyAll(); // jeder blockierte Prozess wird geweckt, prüft seine Wartebedingung
}
synchronized public Object get () {
// ein Konsumenten-Prozess versucht, ein Element zu nehmen
while (buf.isEmpty())
// warten solange der Puffer leer ist
try { wait(); } catch (InterruptedException e) {}
// Jetzt ist es soweit: Es gibt ein Element - das erste kann “geliefert” werden
Object elem = buf.first();
buf.dequeue();
notifyAll();
// jeder blockierte Prozess prüft seine Wartebedingung
return elem;
}
Grundlagen der Programmierung 2, SS 2008
43
Beschränkter Puffer (6) – Details der Lösung
die Produzent-Klasse
Universität Paderborn
Prof. Dr. Stefan Böttcher
class Produzent extends Thread {
String bezeichnung;
int produktionsZahl;
Buffer buf;
Produzent(String ib, int wieviele, Buffer ibuf) { // Konstruktor, Daten übernehmen
bezeichnung = ib; produktionsZahl = wieviele; buf = ibuf; }
public void run() {
// Der Produktionsprozess
while (produktionsZahl > 0) {
Produkt p = new Produkt("Auto");
// Erzeugen eines neuen Autos
System.out.println(bezeichnung+" hat "+p+" produziert");
// Der Produzent möchte es in die Schlange einreihen
buf.put(p);
System.out.println(bezeichnung+" hat "+p+" abgelegt");
try { sleep( (int)( Math.random()*500 ) + 200 ); } // Ausruhen... (200 bis 700 ms)
catch (InterruptedException e) {}
produktionsZahl--;
}
}
}
Grundlagen der Programmierung 2, SS 2008
44
Beschränkter Puffer (7) – Details der Lösung
die Konsument-Klasse
Universität Paderborn
Prof. Dr. Stefan Böttcher
class Konsument extends Thread {
String bezeichnung;
int verbrauchsZahl;
Buffer buf;
Konsument(String ib, int wieviele, Buffer ibuf) { //Konstruktor: Werte übernehmen
bezeichnung = ib; verbrauchsZahl = wieviele; buf = ibuf; }
// Der Konsumentenprozess
public void run() {
while (verbrauchsZahl > 0) {
Produkt p = (Produkt)buf.get();
// Anforderung eines Produktes
System.out.println(bezeichnung+" entnimmt "+p);
try { sleep( (int)( Math.random()*1500 ) + 500 ); } // Verbrauchen (500 ... 2000 ms)
catch (InterruptedException e) {}
verbrauchsZahl--;
}
}
}
Grundlagen der Programmierung 2, SS 2008
45
Beschränkter Puffer (8) – Details der Lösung
Hilfsklassen Queue und Produkt
Universität Paderborn
Prof. Dr. Stefan Böttcher
Klasse Queue (Warteschlange) - Hilfsmittel für den Monitor:
void enqueue(Object o)
void dequeue()
Object first()
boolean isFull()
boolean isEmpty()
- neues Objektes hinten einhängen
- vorderstes Objekt entfernen
- vorderstes Objekt herausgeben
- Test, ob Schlange „voll“ ist (begrenzte Kapazität!)
- Test, ob die Schlange leer ist:
enqueue und dequeue bekommen Ausgabeanweisungen (für Demozwecke)
Hilfsklasse Produkt:
class Produkt {
String bezeichnung;
int id;
static int anzahl = 0; // Klassenvariable zählt Produkt-Objekte von 1...n durch
public String toString() { return bezeichnung+"("+id+")"; } // i-tes heißt “Auto(i)”
Produkt(String ib) { bezeichnung = ib; id = ++anzahl; }
}
Grundlagen der Programmierung 2, SS 2008
46
Universität Paderborn
Prof. Dr. Stefan Böttcher
Synchronisation mit Monitoren - Zusammenfassung
Monitore in Java: Objekte einer Monitor-Klasse
alle kritischen Abschnitte durch synchronized-Methoden codiert
Æ wechselseitiger Ausschluss
Falls Eintritt in den kritischen Abschnitt von Bedingungen abhängt:
wenn Bedingung nicht erfüllt ist, wäre Eintritt in kritischen Abschnitt sinnlos
Æ wait( ) - zur Freigabe des Monitors und warten auf notify ()
wenn sich Bedingungen für andere Prozesse geändert haben (könnten)
Æ notify( ) bzw. notifyAll( ) , um andere Prozesse zu wecken
nach dem Aufwecken muss Bedingung erneut getestet werden
Æ while ( ! Bedingung ) try { wait( ) ; } catch ( InterruptedException e ) { }
Wichtige Beispiele aus dem Bereich Betriebsmitelverwaltung:
1. eine Schwellwert-Bedingung (z.B. Resourcen-Verleih: vorhanden >0)
2. zwei Schwellwerte: (z.B. begrenzter Puffer: 0 ≤ Füllung und Füllung ≤ max)
Grundlagen der Programmierung 2, SS 2008
47
Herunterladen