22.01.2014 Aufrufe

2 Nebenläufigkeit, Threads und Synchronisation

2 Nebenläufigkeit, Threads und Synchronisation

2 Nebenläufigkeit, Threads und Synchronisation

MEHR ANZEIGEN
WENIGER ANZEIGEN

Erfolgreiche ePaper selbst erstellen

Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.

(107)<br />

2 Nebenläufigkeit, <strong>Threads</strong> <strong>und</strong> <strong>Synchronisation</strong><br />

Gliederung:<br />

• Parallele Prozesse<br />

• Java-<strong>Threads</strong><br />

• <strong>Synchronisation</strong>, gegenseitiger Ausschluß<br />

• Das Monitorkonzept<br />

• Deadlocks<br />

2.1 Parallele Prozesse<br />

Prozess: Ausführung eines sequentiellen Programmstückes in dem zugeordneten Speicher<br />

(Adressraum). Veränderlicher Zustand: Speicherinhalt <strong>und</strong> Programmposition.<br />

Parallele Prozesse: mehrere Prozesse, die gleichzeitig auf mehreren Prozessoren ausgeführt<br />

werden.<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus<br />

(108)<br />

Verzahnte Ausführung kann parallele Ausführung simulieren. Häufiger Wechsel vermittelt den<br />

Eindruck, dass alle Prozesse gleichmäßig fortschreiten.<br />

Nebenläufige Prozesse: Prozesse, die parallel oder verzahnt ausgeführt werden können.<br />

<strong>Threads</strong> („Fäden“ der Programmausführung): Prozesse, die parallel oder verzahnt in<br />

gemeinsamem Speicher ablaufen. Die Prozessumschaltung ist besonders einfach <strong>und</strong> schnell.<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus


(109)<br />

Anwendungen paralleler Prozesse:<br />

• Benutzungsoberflächen:<br />

Die Ereignisse werden von einem speziellen Systemprozess weitergegeben. Aufwendige<br />

Berechnungen sollten nebenläufig programmiert werden, damit die Bedienung der Oberfläche<br />

nicht blockiert wird.<br />

• Simulation realer Abläufe:<br />

z. B. Produktion in einer Fabrik<br />

• Animation:<br />

Veranschaulichung von Abläufen, Algorithmen; Spiele<br />

• Steuerung von Geräten:<br />

Prozesse im Rechner überwachen <strong>und</strong> steuern externe Geräte, z. B. Montage-Roboter<br />

• Leistungssteigerung durch Parallelrechner:<br />

mehrere Prozesse bearbeiten gemeinsam die gleiche Aufgabe, z. B. paralleles Sortieren großer<br />

Datenmengen.<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus<br />

(110)<br />

2.2 Java-<strong>Threads</strong><br />

Begriff „<strong>Threads</strong>“: Prozesse, die nebenläufig, gemeinsam im Speicher des Programms ablaufen.<br />

Zwei Techniken zur Thread-Erzeugung:<br />

1. Nutzung des Interface Runnable<br />

2. Erben von der Thread-Klasse<br />

Erste Technik:<br />

Eine Benutzerklasse B implementiert das Interface Runnable<br />

verlangt das Vorhandensein einer Methode run()<br />

Erzeugung eines <strong>Threads</strong> t mit einer Instanz von B als Parameter<br />

Starten des <strong>Threads</strong> mit t.start();<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus


(111)<br />

a) Eine Benutzerklasse implementiert das Interface Runnable:<br />

class Aufgabe implements Runnable {<br />

...<br />

public void run () {<br />

// vom Interface geforderte Methode run<br />

... // das als Prozess auszuführende Programmstück<br />

}<br />

Aufgabe (...) {...}<br />

// Konstruktor<br />

}<br />

b) Der eigentliche Prozess wird als Objekt der vordefinierten Klasse Thread erzeugt:<br />

Thread auftrag = new Thread (new Aufgabe (...));<br />

c) Erst folgender Aufruf startet dann den Prozess:<br />

auftrag.start(); //Der neue Prozess beginnt, neben dem hier aktiven zu laufen.<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus<br />

Einfaches Beispiel:<br />

Einem Prozess übergibt man eine ganze Zahl n, dieser druckt n-mal diese Zahl aus <strong>und</strong><br />

hört dann auf.<br />

class T implements Runnable {<br />

private String name;<br />

private int anzahl;<br />

public void run() {<br />

System.out.print("Thread "+name+" fängt an / ");<br />

for (int i=0; i


(113)<br />

Im Hauptprogramm werden vier <strong>Threads</strong> aus den „Runnable“-Objekten vom Typ T mit<br />

verschiedenen Parametern erzeugt <strong>und</strong> gestartet:<br />

class TTest1 {<br />

public static void main (String [] args) {<br />

Thread t1 = new Thread(new T("1", 10));<br />

Thread t2 = new Thread(new T("2", 2));<br />

Thread t3 = new Thread(new T("3", 4));<br />

Thread t4 = new Thread(new T("4", 1));<br />

t1.start(); t2.start(); t3.start(); t4.start();<br />

}<br />

}<br />

Ergebnis (ein mögliches – jeder Programmlauf zeigt eine andere zufällige Reihenfolge):<br />

Thread 3 fängt an / 3333 Thread 4 fängt an / 4 Thread 1 fängt an / Thread 2<br />

fängt an / 221 / Thread 3 hört auf / Thread 4 hört auf / Thread 2 hört auf /<br />

111111111 / Thread 1 hört auf /<br />

[3 3 3 3 3 [4 4 [1 [2 2 2 1 3] 4] 2] 1 1 1 1 1 1 1 1 1 1]<br />

1: [ + + + + + + + + + + ]<br />

2: [ + + ]<br />

3: [ + + + + ]<br />

4: [ + ]<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus<br />

Zweite Technik:<br />

Eine Klasse B wird als Unterklasse der vordefinierten Klasse Thread definiert<br />

Überschreiben der Methode run()<br />

Starten einer Instanz t des <strong>Threads</strong> mit Aufruf der geerbten Methode t.start();<br />

a) Die Benutzerklasse wird als Unterklasse der vordefinierten Klasse Thread definiert:<br />

class T extends Thread {<br />

...<br />

public void run () {<br />

//überschreibt die Thread-Methode run()<br />

...<br />

} //das als Prozess auszuführende Programmstück<br />

(114)<br />

}<br />

T (...) {...}<br />

// Konstruktor<br />

b) Der Prozess wird als Objekt der Benutzerklasse erzeugt (es ist auch ein Thread-Objekt):<br />

Thread t = new T (...);<br />

c) Erst folgender Aufruf startet dann den Prozess:<br />

t.start();<br />

//der neue Prozess beginnt neben dem hier aktiven zu laufen<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus


(115)<br />

Einfaches Beispiel nochmal:<br />

class T extends Thread { ... }<br />

// Alles andere genau wie vorher!<br />

Im Hauptprogramm werden vier T-Objekte direkt als Thread eingesetzt:<br />

class TTest2 {<br />

public static void main (String [] args) {<br />

Thread t1 = new T("1", 10);<br />

Thread t2 = new T("2", 2);<br />

Thread t3 = new T("3", 4);<br />

Thread t4 = new T("4", 1);<br />

t1.start(); t2.start(); t3.start(); t4.start();<br />

}<br />

}<br />

TTest2.java<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus<br />

Gegenüberstellung<br />

(116)<br />

class T implements Runnable {<br />

...<br />

public void run() {<br />

// Prozess<br />

}<br />

...<br />

}<br />

Innerhalb der Klasse T stehen die Methoden<br />

von Thread nicht unmittelbar zur Verfügung<br />

class T extends Thread {<br />

...<br />

public void run() {<br />

// Prozess<br />

}<br />

...<br />

}<br />

Innerhalb der Klasse T stehen die Methoden<br />

von Thread zur Verfügung<br />

Thread t = new Thread(new T(...)); Thread t = new T(...);<br />

t.start();<br />

t.start();<br />

• Klasse Thread verwaltet die Ausführung der run-Methode im Zusammenspiel mit allen<br />

anderen Prozessen<br />

• Methode zum Ändern des dynamischen Zustands des Prozesses<br />

• Prioritäten, gegenseitigen Ausschluss, Signalisieren von Bedingungen, Exceptions<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus


(117)<br />

• Unterliegendes Zustandsmodell für <strong>Threads</strong>:<br />

o runnable: Prozess läuft<br />

o waiting: Prozess wartet auf einen anderen Prozess (Bedingung)<br />

o timed waiting: Prozess wartet für eine bestimmte Zeit<br />

o terminated: Prozess ist<br />

abgeschlossen<br />

new<br />

Programm startet den Thread<br />

Weitermachenl<br />

runnable<br />

Thread ist fertig<br />

terminated<br />

Warten<br />

Schlafen legen<br />

Zeitintervall<br />

abgelaufen<br />

waiting<br />

timed<br />

waiting<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus<br />

Zweites Beispiel: Eine Digitaluhr<br />

Eigenschaft: Aktualisieren der Anzeige alle 1000ms<br />

• Kurzfristig anhalten mit STOP<br />

• Weiterlaufenlassen mit GO<br />

• Ganz beenden mit dem Schliesse-Button<br />

Steuerung der Uhr mit der Variablen running:<br />

boolean running == true genau dann, wenn die Uhr "läuft"<br />

= Anzeige wechselt jede Sek<strong>und</strong>e<br />

Gr<strong>und</strong>struktur des Programms mit den zwei Klassen<br />

TDigiClock der Thread, der die Änderungen des anzuzeigenden Strings berechnet<br />

DigiClock der anzeigende JFrame, gleichzeitig Hauptklasse<br />

(118)<br />

DigiClock.java<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus


(119)<br />

Thread TDigiClock, der die Anzeige setzt<br />

class TDigiClock implements Runnable {<br />

private boolean running;<br />

DigiClock jfDigiClock;<br />

// Referenz auf den JFrame, der die Uhr anzeigt<br />

public TDigiClock (DigiClock jfDigiClock) {<br />

this.jfDigiClock = jfDigiClock; }<br />

public void run() {<br />

running = true;<br />

while (running) {<br />

jfDigiClock.setAnzeige<br />

new Date().toString().substring(11,19));<br />

try { Thread.sleep(1000); }<br />

catch (InterruptedException ex) { }<br />

}<br />

}<br />

}<br />

public void endIt() { running = false; }<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus<br />

Die Hauptklasse (Fenster, Buttons, Anzeige):<br />

class DigiClock extends JFrame implements ActionListener<br />

{ JLabel anzeige = new JLabel(" ");<br />

JButton stop = new JButton ("STOP!");<br />

JButton go = new JButton ("GO");<br />

JPanel buttons = new JPanel();<br />

// Initialisierungen im Konstruktor<br />

TDigiClock clock = new TDigiClock(this); // Anlegen eines Uhr-<strong>Threads</strong><br />

void setAnzeige(String s) { anzeige.setText(s); }<br />

// Methode zum Setzen der Anzeige, aufgerufen vom DigiClock-Thread<br />

public void actionPerformed (ActionEvent e) { // Button-Funktionen<br />

if (e.getActionCommand().equals("go")) {<br />

go.setEnabled(false);<br />

stop.setEnabled(true);<br />

new Thread(clock).start(); // Starten eines neuen <strong>Threads</strong>, der alle<br />

} // 1000ms die Anzeige setzt<br />

else {<br />

stop.setEnabled(false);<br />

go.setEnabled(true);<br />

clock.endIt();<br />

// setzt running = false, Prozess endet<br />

}<br />

}<br />

(120)<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus


DigiClock() {<br />

// Konstruktor des JFrames<br />

getContentPane().setLayout(new BorderLayout());<br />

anzeige.setHorizontalAlignment(JLabel.CENTER);<br />

anzeige.setFont(new Font("Times-Roman", Font.BOLD, 72));<br />

getContentPane().add(anzeige, BorderLayout.NORTH);<br />

buttons.setLayout(new GridLayout(1,2));<br />

stop.addActionListener(this);<br />

stop.setEnabled(true);<br />

buttons.add(stop);<br />

go.addActionListener(this);<br />

go.setEnabled(false);<br />

buttons.add(go);<br />

(121)<br />

}<br />

getContentPane().add(buttons, BorderLayout.SOUTH);<br />

...<br />

public static void main (String [] args) { // Triviales Hauptprogramm<br />

new TDigiClock();<br />

}<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus<br />

Wichtige Methoden der Klasse Thread:<br />

(122)<br />

public void run ()<br />

public void start ()<br />

public void join ()<br />

throws InterruptedException<br />

public static void sleep<br />

(long millisec)<br />

throws InterruptedException<br />

wird überschrieben mit der Methode, die die<br />

auszuführenden Anweisungen enthält<br />

startet die Ausführung des Prozesses, nur einmal<br />

pro Thread möglich!<br />

der aufrufende Prozess wartet bis der angegebene<br />

Prozess terminiert ist:<br />

try { auftrag.join(); }<br />

catch (InterruptedException e){}<br />

der aufrufende Prozess wartet mindestens die in<br />

Millisek<strong>und</strong>en angegebene Zeit:<br />

try { Thread.sleep (1000); }<br />

catch (InterruptedException e){}<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus


(123)<br />

Drittes Beispiel: Prozesse warten auf andere<br />

Variante von TTest2<br />

• Drucken von Zeichen, dazwischen aber Übergang in den Schlafmodus für eine definierte<br />

"sleep time"<br />

• Übergabe eines anderen <strong>Threads</strong>, auf den mit dem Abschluss ggf. (!) gewartet wird<br />

class T extends Thread {<br />

private String name;<br />

private int anzahl, sleepTime;<br />

private Thread wartetAuf;<br />

public void run() {<br />

System.out.println(" Thread "+name+" faengt an / ");<br />

for (int i=0; i


(125)<br />

2.3 <strong>Synchronisation</strong>, gegenseitiger Ausschluss, Monitorkonzept<br />

2.3.1 <strong>Synchronisation</strong> von Prozessen über gegenseitigen Ausschluss<br />

Wenn mehrere Prozesse die Werte gemeinsamer Variablen verändern, kann eine ungünstige<br />

Verzahnung (Parallelausführung) zu inkonsistenten Daten führen.<br />

Z. B. zwei Prozesse p <strong>und</strong> q benutzen lokale Variable tmp <strong>und</strong> eine gemeinsame Variable konto:<br />

Prozeß p tmp = konto;<br />

konto = tmp-300;<br />

Prozeß q tmp = konto; konto = tmp + 1000;<br />

p holt sich Kontostand 400 €, q auch, q schreibt 1400 €, p überschreibt mit 100 € - 1300 € futsch...!<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus<br />

• Kritische Abschnitte: zusammengesetzte Operationen, die gemeinsame Variablen lesen<br />

<strong>und</strong>/oder verändern.<br />

Prozesse müssen kritische Abschnitte unter gegenseitigem Ausschluß ("mutual exclusion")<br />

ausführen:<br />

D. h. zu jedem Zeitpunkt darf höchstens ein Prozess solch einen kritischen Abschnitt für<br />

bestimmte Variablen ausführen.<br />

(126)<br />

Andere Prozesse, die für die gleichen Variablen mit der Ausführung eines kritischen Abschnitts<br />

beginnen wollen, müssen warten.<br />

Experiment: Ist es wirklich so schlimm?<br />

Java-Programm „SynchTest“<br />

SynchTest.java<br />

• Zwei Prozesse, jeder führt 5 Buchungen durch, laufen nebenläufig ohne Kontrolle<br />

• Jeder Prozess hat eine individuelle Bearbeitungsdauer <strong>und</strong> Wartezeit zwischen je zwei<br />

Buchungen<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus


(127)<br />

class Ablauf extends Thread {<br />

private int tmp;<br />

private int betrag;<br />

private int zeitBedarf, zeitAbstand;<br />

private String name;<br />

Ablauf (String name, int zeitBedarf, int zeitAbstand, int betrag)<br />

{ ... }<br />

public void run() {<br />

for (int i=0; i


(129)<br />

2.3.2 Das Monitorkonzept<br />

• Monitor<br />

Ein Modul, der Daten <strong>und</strong> die Operationen darauf kapselt.<br />

Die kritischen Abschnitte auf den Daten werden als Monitor-Operationen formuliert.<br />

Prozesse rufen Monitor-Operationen auf, um auf die Daten zuzugreifen.<br />

Die Monitor-Operationen werden unter gegenseitigem Ausschluss ausgeführt.<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus<br />

• Monitore in Java:<br />

Anweisungsblöcke oder Methoden einer Klasse, die kritische Abschnitte auf Objektvariablen<br />

implementieren, können als synchronized gekennzeichnet werden:<br />

class Bank {<br />

synchronized public void kontoBewegung (...) {...}<br />

...<br />

private int[] konten;<br />

}<br />

Jedes Objekt der Klasse wirkt als Monitor für seine (!) Objektvariablen:<br />

Aufrufe von synchronized Methoden von mehreren Prozessen für dasselbe<br />

Objekt werden unter gegenseitigem Ausschluss ausgeführt.<br />

Zu jedem Zeitpunkt kann höchstens 1 Prozess mit synchronized Methoden auf<br />

Objektvariable zugreifen.<br />

(130)<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus


(131)<br />

Nochmal unser Experiment (erst ohne Monitor, dann mit Monitor):<br />

class Konto {<br />

private int stand = 0;<br />

// Eine Klasse für die Konten – jedes Konto ein Objekt<br />

}<br />

public void bewegung(String name, int betrag) // KRITISCHER ABSCHNITT<br />

{<br />

int tmp;<br />

tmp = stand;<br />

System.out.println(name+": Gelesener Kontostand "+ tmp);<br />

tmp = tmp + betrag;<br />

System.out.println(name+": Geschriebener Kontostand "+ tmp);<br />

stand = tmp;<br />

}<br />

• Die Ablauf-Klasse beschreibt einen Prozess, der fünfmal das Gleiche bucht<br />

• Im Hauptprogramm wird ein Konto angelegt <strong>und</strong> zwei Abläufe werden darauf losgelassen<br />

• Wieder: Übergabe von Bearbeitungszeit <strong>und</strong> Wartezeit<br />

OhneMonitorTest.java<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus<br />

(132)<br />

• Änderung: Schlüsselwort synchronized bei bewegung(...):<br />

synchronized public void bewegung(String name, int betrag) {...}<br />

• Methode kann nun von maximal einem Prozess zu einer Zeit ausgeführt werden!<br />

• Korrekter Ablauf, unabhängig von den Zeitangaben!<br />

MonitorTest.java<br />

Eigenschaft dieses Beispiels:<br />

• Sobald die "Ressource" Konto frei ist, kann jeder der beiden Ablauf-Objekte diese nutzen,<br />

es gibt keine Vorbedingung!<br />

• Daher reicht die Sicherstellung, dass kein doppelter Zugriff im kritischen Abschnitt passiert.<br />

• Das reicht aber nicht immer...<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus


(133)<br />

2.3.3 Monitore mit Bedingungssynchronisation<br />

• Bedingungssynchronisation:<br />

Ein Prozess wartet, bis eine Bedingung erfüllt ist - verursacht durch einen anderen Prozess;<br />

z. B. erst dann in einen Puffer schreiben, wenn darin Platz ist.<br />

• Bedingungssynchronisation im Monitor:<br />

Ein Prozess, der in einer kritischen Monitor-Operation auf eine Bedingung wartet, muss den<br />

Monitor freigeben, damit andere Prozesse den Zustand des Monitors ändern können.<br />

• Bedingungssynchronisation in Java:<br />

Vordefinierte Methoden der Klasse Object zur Bedingungssynchronisation:<br />

(Sie müssen aus synchronized Abschnitten heraus aufgerufen werden.)<br />

wait() blockiert den aufrufenden Prozess <strong>und</strong> gibt den Monitor frei - das<br />

Objekt, dessen synchronized Methode er gerade ausführt.<br />

notify() weckt einen der in diesem Monitor blockierten Prozesse; welcher ist<br />

nicht definiert.<br />

notifyAll() weckt alle in diesem Monitor blockierten Prozesse; sie können<br />

weiterlaufen, sobald der Monitor frei ist.<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus<br />

• Gr<strong>und</strong>struktur:<br />

a) Anforderung<br />

Solange meine Anforderung nicht erfüllt: wait(); //Hier geht’s nur weiter, wenn<br />

//andere mich "benachrichtigen"<br />

Wenn Anforderung jetzt erfüllt: Ressourcen nutzen.<br />

Sonst: wieder warten!<br />

b) Rückgabe<br />

Ressourcen zurückgeben<br />

Allen Wartenden sagen, dass sie es jetzt wieder probieren können: notifyAll();<br />

Wichtig bei a):<br />

Nachdem ein blockierter Prozess geweckt wurde, muss er die Bedingung, auf die er wartet,<br />

erneut prüfen - sie könnte schon durch schnellere Prozesse wieder ungültig sein:<br />

while (!erfüllt) try { wait(); } catch (InterruptedException e) {}<br />

(134)<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus


(135)<br />

Monitor zur Vergabe von Ressourcen („Betriebsmitteln“):<br />

Aufgabe: Eine begrenzte Anzahl gleichartiger Ressourcen wird von einem Monitor verwaltet.<br />

Prozesse fordern einige Ressourcen an <strong>und</strong> geben sie später zurück.<br />

Programmstruktur:<br />

• Eine Klasse Monitor - verwaltet die verfügbaren Ressourcen (zählen genügt)<br />

• Eine Klasse Verbraucher (Unterklasse von Thread), Verbraucherprozess - mehrfach<br />

instanziiert; alle Verbraucher „streiten“ sich um die Ressourcen<br />

• Ein Hauptprogramm: legt fest, wie viele Exemplare der Ressource es gibt, startet die<br />

Verbraucher.<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus<br />

Der Monitor:<br />

class Monitor {<br />

// Anzahl der verfügbaren Ressourcen, diese Variable<br />

private int vorhanden;<br />

// wird überwacht!<br />

Monitor(int v) { vorhanden = v; } // Initialisierung mit der Maximalzahl<br />

synchronized void anfordern(int n, int wer) { ... }<br />

// Mit dieser Anfrage an den Monitor fordert der Verbraucher “wer”<br />

// insgesamt n Elemente der Ressource an<br />

(136)<br />

}<br />

synchronized void freigeben(int n, int wer) { ... }<br />

// Mit dieser Anfrage gibt der Verbraucher “wer” die von ihm gebrauchten<br />

// insgesamt n Elemente der Ressourcen zurück<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus


Verbraucher Nummer “wer” fordert “n” Ressourcen an<br />

synchronized void anfordern(int n, int wer)<br />

(137)<br />

}<br />

// solange nicht genug verfügbar sind...<br />

while (vorhanden < n)<br />

{ // geht der Prozeß in Wartestellung <strong>und</strong> erwartet, dass man ihn “weckt”<br />

try { wait(); } catch(InterruptedException e) {}<br />

}<br />

// Jetzt ist die while-Schleife zu Ende: Es sind genug Ressourcen vorhanden!<br />

vorhanden -= n;<br />

// Her damit!<br />

// Die Rückgabe: Verbraucher “wer” gibt “n” Ressourcen zurück<br />

synchronized void freigeben(int n, int wer) {<br />

vorhanden += n;<br />

notifyAll();<br />

// Die eigentliche Rückgabe<br />

// Alle wartenden Prozesse werden informiert: Weitermachen<br />

}<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus<br />

(138)<br />

Der Verbraucherprozeß:<br />

class Verbraucher extends Thread {<br />

private Monitor mon; // Der Verbraucher merkt sich, welcher Monitor ihn<br />

bedient<br />

private int ident, // Identifikation des Prozesses (0, 1, 2, ...)<br />

wieOft, // Wie oft fordert der Prozess Ressourcen an?<br />

max; // Wie viele Ressourcen werden maximal angefordert?<br />

Verbraucher(...) {...}<br />

// Der übliche Konstruktor<br />

}<br />

public void run()<br />

{ while (wieOft > 0) // Iteration über die Anforderungen<br />

{ // Wieviel Ressourcen werden angefordert (zwischen 1 <strong>und</strong> max)<br />

int anforderung = (int)( Math.random()*(max-1) ) + 1;<br />

mon.anfordern(anforderung, ident); // Die Anforderung<br />

// Verwendung der Ressource für 1...1000 ms (Zufall)<br />

try { sleep( (int)( Math.random()*999 ) + 1); }<br />

catch(InterruptedException e) {}<br />

mon.freigeben(anforderung, ident);<br />

// Die Freigabe<br />

wieOft--;<br />

}<br />

}<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus


(139)<br />

Das Hauptprogramm:<br />

class MonitorMitRessourcen {<br />

public static void main(String [] args) {<br />

int vorhanden = 20;<br />

// Maximal sind 20 Ressourcen verfügbar<br />

Monitor mon = new Monitor (vorhanden);<br />

}<br />

// Erzeugung <strong>und</strong> starten von 5 Verbrauchern, die jeweils 4 Anforderungen erzeugen<br />

for (int i=0; i < 5; i++)<br />

new Verbraucher(mon, i, 4, vorhanden).start();<br />

// Alle Verbraucher können maximal "vorhanden" viele Ressourcen anfordern<br />

}<br />

MonitorMitRessourcen.java<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus<br />

Ein typischer Ablauf:<br />

Verbraucher 0 fordert 15 Elemente an.<br />

Verbraucher 0 bekommt 15 Elemente. Jetzt verfügbar: 5<br />

Verbraucher 1 fordert 5 Elemente an.<br />

Verbraucher 1 bekommt 5 Elemente. Jetzt verfügbar: 0<br />

Verbraucher 2 fordert 19 Elemente an.<br />

Verbraucher 3 fordert 9 Elemente an.<br />

Verbraucher 4 fordert 13 Elemente an.<br />

Verbraucher 1 gibt 5 Elemente frei. Jetzt verfügbar: 5<br />

Verbraucher 1 fordert 17 Elemente an.<br />

Verbraucher 0 gibt 15 Elemente frei. Jetzt verfügbar: 20<br />

Verbraucher 0 fordert 19 Elemente an.<br />

Verbraucher 0 bekommt 19 Elemente. Jetzt verfügbar: 1<br />

Verbraucher 0 gibt 19 Elemente frei. Jetzt verfügbar: 20<br />

Verbraucher 0 fordert 2 Elemente an.<br />

Verbraucher 0 bekommt 2 Elemente. Jetzt verfügbar: 18<br />

Verbraucher 1 bekommt 17 Elemente. Jetzt verfügbar: 1<br />

Verbraucher 0 gibt 2 Elemente frei. Jetzt verfügbar: 3<br />

Verbraucher 0 fordert 5 Elemente an.<br />

Verbraucher 1 gibt 17 Elemente frei. Jetzt verfügbar: 20<br />

Verbraucher 1 fordert 2 Elemente an.<br />

Verbraucher 1 bekommt 2 Elemente. Jetzt verfügbar: 18<br />

Verbraucher 4 bekommt 13 Elemente. Jetzt verfügbar: 5<br />

...<br />

(140)<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus


Schema: Gleichartige Ressourcen vergeben<br />

Ein Monitor verwaltet eine endliche Menge von k ≥ 1 gleichartigen Ressourcen.<br />

Prozesse fordern jeweils unterschiedlich viele (n) Ressourcen an, 1 ≤ n ≤ k, <strong>und</strong> geben sie<br />

nach Gebrauch wieder zurück.<br />

Die Wartebedingung für das Anfordern ist „Sind n Ressourcen verfügbar?“<br />

Zu jedem Zeitpunkt zwischen Aufrufen der Monitor-Methoden gilt:<br />

„Die Summe der freien <strong>und</strong> der vergebenen Ressourcen ist k.“<br />

Beispiele:<br />

• Walkman-Vergabe an Besuchergruppen im Museum<br />

• Vergabe von Parkplätzen<br />

• Taxi-Unternehmen; n = 1<br />

auch abstrakte Ressourcen, z. B.<br />

• das Recht, eine Brücke begrenzter Kapazität (Anzahl Fahrzeuge oder Gewicht) zu befahren.<br />

auch gleichartige Ressourcen mit Identität:<br />

• Der Monitor muss dann die Identitäten der Ressourcen in einer Datenstruktur verwalten.<br />

• Nummern der vom Taxi-Unternehmen vergebenen Taxis<br />

• Adressen der Speicherblöcke, die eine Speicherverwaltung vergibt<br />

(141)<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus<br />

Schema: Beschränkter Puffer<br />

Ein Monitor speichert Elemente in einem Puffer von beschränkter Größe.<br />

Produzenten-Prozesse liefern einzelne Elemente,<br />

Konsumenten-Prozesse entnehmen einzelne Elemente.<br />

Der Monitor stellt sicher, dass die Elemente in der gleichen Reihenfolge geliefert <strong>und</strong><br />

entnommen werden. (Datenstruktur: Schlange, Queue)<br />

Die Wartebedingungen<br />

lauten:<br />

• in den Puffer schreiben,<br />

nur wenn er nicht voll<br />

ist,<br />

• aus dem Puffer<br />

nehmen, nur wenn er<br />

nicht leer ist.<br />

(142)<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus


(143)<br />

Was brauchen wir?<br />

• Eine Klasse Monitor - die verwaltet die verfügbaren Ressourcen (als Individuen in einer<br />

Warteschlange)<br />

• Eine Klasse Produzent (Unterklasse von Thread) - mehrfach instanziiert; alle<br />

Produzenten wollen ihre „Produkte“ in die Schlange schreiben<br />

(Operationen enqueue - einspeichern, dequeue – entfernen)<br />

• Eine Klasse Verbraucher (Unterklasse von Thread) - mehrfach instanziiert; alle<br />

Verbraucher „streiten“ sich um die in der Schlange verwalteten Produkte<br />

• Ein Hauptprogramm: startet Produzenten <strong>und</strong> Verbraucher<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus<br />

Monitor-Klasse für beschränkte Puffer:<br />

(144)<br />

class Buffer {<br />

private Queue buf;<br />

// Schlange der Länge n zur Aufnahme der Elemente<br />

public Buffer (int n) { buf = new Queue(n); }<br />

// Die Größe n der Schlange modelliert die Begrenztheit des Puffers<br />

synchronized public void put (Object elem) { ... }<br />

// Methode zum Ablegen eines neu produzierten Elementes<br />

}<br />

synchronized public Object get () { ... }<br />

// Methode zum Abholen (Verbrauchen) eines Elementes<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus


(145)<br />

synchronized public void put (Object elem) {<br />

// ein Produzenten-Prozess versucht, ein Element zu liefern<br />

while (buf.isFull()) {<br />

// warten solange der Puffer voll ist<br />

try { wait(); } catch (InterruptedException e) {}<br />

}<br />

// Jetzt ist es soweit: Puffer ist nicht mehr voll<br />

buf.enqueue (elem);<br />

notifyAll(); // jeder blockierte Prozess prüft seine Wartebedingung<br />

synchronized public Object get () {<br />

// ein Konsumenten-Prozess versucht, ein Element zu nehmen<br />

while (buf.isEmpty())<br />

// warten solange der Puffer leer ist<br />

try { wait(); } catch (InterruptedException e) {}<br />

// Jetzt ist es soweit: Es gibt ein Element - das erste kann “geliefert” werden<br />

Object elem = buf.first();<br />

buf.dequeue();<br />

notifyAll();<br />

// jeder blockierte Prozess prüft seine Wartebedingung<br />

return elem;<br />

}<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus<br />

Die Produzenten-Klasse:<br />

(146)<br />

class Produzent extends Thread {<br />

String bezeichnung;<br />

int produktionsZahl;<br />

Buffer buf;<br />

// Konstruktor, Übernahme der Gr<strong>und</strong>daten<br />

Produzent(String ib, int wieviele, Buffer ibuf) {<br />

bezeichnung = ib; produktionsZahl = wieviele; buf = ibuf; }<br />

}<br />

public void run() {<br />

// Der Produktionsprozess<br />

while (produktionsZahl > 0) {<br />

Produkt p = new Produkt("Auto"); // Erzeugen eines neuen Autos<br />

System.out.println(bezeichnung+" hat "+p+" produziert");<br />

// Der Produzent möchte es in die Schlange einreihen<br />

buf.put(p);<br />

System.out.println(bezeichnung+" hat "+p+" abgelegt");<br />

// Ausruhen... (200 bis 700 ms)<br />

try { sleep( (int)( Math.random()*500 ) + 200 ); }<br />

catch (InterruptedException e) {}<br />

produktionsZahl--;<br />

}<br />

}<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus


(147)<br />

Die Konsumenten-Klasse<br />

class Konsument extends Thread {<br />

String bezeichnung;<br />

int verbrauchsZahl;<br />

Buffer buf;<br />

//Konstruktor, Übernahme der Gr<strong>und</strong>daten<br />

Konsument(String ib, int wieviele, Buffer ibuf) {<br />

bezeichnung = ib; verbrauchsZahl = wieviele; buf = ibuf; }<br />

// Der Konsumprozess<br />

public void run() {<br />

while (verbrauchsZahl > 0) {<br />

Produkt p = (Produkt)buf.get(); // Anforderung eines Produktes<br />

System.out.println(bezeichnung+" entnimmt "+p);<br />

// Verbrauchen (500 ... 2000 ms)<br />

try { sleep( (int)( Math.random()*1500 ) + 500 ); }<br />

catch (InterruptedException e) {}<br />

verbrauchsZahl--;<br />

}<br />

}<br />

}<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus<br />

Die Warteschlange Queue als Hilfsmittel für den Monitor:<br />

Kennt folgende Methoden<br />

• Einhängen eines neuen Objektes (ganz hinten anstellen!):<br />

void enqueue(Object o)<br />

• Entfernen des vordersten Objektes:<br />

void dequeue()<br />

• Angabe des vordersten Objektes:<br />

Object first()<br />

• Test, ob die Schlange „voll“ ist (begrenzte Kapazität!):<br />

boolean isFull()<br />

• Test, ob die Schlange leer ist:<br />

boolean isEmpty()<br />

Queue-Methoden werden angereichtert um Ausgabeanweisungen bei enqueue <strong>und</strong> dequeue<br />

(damit man sieht was passiert)<br />

(148)<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus


(149)<br />

Hilfsklasse Produkt:<br />

class Produkt {<br />

String bezeichnung;<br />

int id;<br />

// Klassenvariable zählt einfach alle Produkt-Objekte von 1...n durch<br />

static int anzahl = 0;<br />

// Das i-te Produkt heißt “Auto(i)”<br />

public String toString() { return bezeichnung+"("+id+")"; }<br />

}<br />

Produkt(String ib) { bezeichnung = ib; id = ++anzahl; }<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus<br />

Hauptprogramm:<br />

(150)<br />

class MonitorMitPuffer {<br />

public static void main(String [] args) {<br />

Buffer puffer = new Buffer(20);<br />

// Lager nimmt max. 20 Autos auf<br />

// Die Produzenten <strong>und</strong> ihre Leistungsfähigkeit: 10 VWs, 3 BMWs, ...<br />

new Produzent("VW", 10, puffer).start();<br />

new Produzent("BMW", 3, puffer).start();<br />

new Produzent("Mercedes", 2, puffer).start();<br />

new Produzent("Trabant", 18, puffer).start();<br />

}<br />

// Die Konsumenten <strong>und</strong> ihre Gier: 3 Autos für Heike, 10 für Thomek, ... :-)<br />

new Konsument("Andreas", 3, puffer).start();<br />

new Konsument("Thomek", 10, puffer).start();<br />

new Konsument("Gero", 5, puffer).start();<br />

new Konsument("Gunnar", 4, puffer).start();<br />

new Konsument("Gerd", 8, puffer).start();<br />

// Es werden 33 Autos hergestellt <strong>und</strong> 30 abgenommen<br />

}<br />

MonitorMitPuffer.java<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus


(151)<br />

Typisches Ergebnis (links: Puffergröße 20 – rechts: Puffergröße 2)<br />

VW hat Auto(1) produziert<br />

Einspeichern von Auto(1) Anzahl: 1<br />

VW hat Auto(1) abgelegt<br />

BMW hat Auto(2) produziert<br />

Einspeichern von Auto(2) Anzahl: 2<br />

BMW hat Auto(2) abgelegt<br />

Mercedes hat Auto(3) produziert<br />

Einspeichern von Auto(3) Anzahl: 3<br />

Mercedes hat Auto(3) abgelegt<br />

Trabant hat Auto(4) produziert<br />

Einspeichern von Auto(4) Anzahl: 4<br />

Trabant hat Auto(4) abgelegt<br />

Entnahme von Auto(1) Anzahl: 4<br />

Andreas entnimmt Auto(1)<br />

Entnahme von Auto(2) Anzahl: 3<br />

Thomek entnimmt Auto(2)<br />

Entnahme von Auto(3) Anzahl: 2<br />

Gero entnimmt Auto(3)<br />

......<br />

VW hat Auto(1) produziert<br />

Einspeichern von Auto(1) Anzahl: 1<br />

VW hat Auto(1) abgelegt<br />

BMW hat Auto(2) produziert<br />

Einspeichern von Auto(2) Anzahl: 2<br />

BMW hat Auto(2) abgelegt<br />

Mercedes hat Auto(3) produziert<br />

Trabant hat Auto(4) produziert<br />

Entnahme von Auto(1) Anzahl: 2<br />

Einspeichern von Auto(3) Anzahl: 2<br />

Mercedes hat Auto(3) abgelegt<br />

Andreas entnimmt Auto(1)<br />

Entnahme von Auto(2) Anzahl: 2<br />

Einspeichern von Auto(4) Anzahl: 2<br />

Trabant hat Auto(4) abgelegt<br />

....<br />

©2007 Prof. Dr. Gerd Szwillus<br />

Vorlesung „Gr<strong>und</strong>lagen der Programmierung 2“, SS 2007, Gerd Szwillus

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!