24.5
Monitoring - Locking |
||
T. Hoare erfand u.a. den Quick-Sort-Algorithmus
|
Wir haben gelernt,
dass man Threads dazu benutzt, nebenläufige Prozesse zu programmieren.
Stellen wir uns einmal folgende Situation vor: Zwei Personen, nennen wir
sie Herr Fritz und Frau Annerose, gehen im gleichen Supermarkt einkaufen.
Sicher sind das zwei unabhängige Prozesse, die auch unterschiedlich lange
dauern können. Beide benutzen für ihren Einkauf einen Einkaufswagen. Es
ist klar, wenn Herr Fritz einen Einkaufswagen benutzt, kann dieser nicht
gleichzeitig von Frau Anneliese benutzt werden. Sie benutzt einen anderen
oder, falls keiner mehr frei ist, wartet sie bis wieder ein Einkaufswagen
frei wird. Versuchen wir die Situation in einem Programm zu modellieren, würden wir
die (Supermarkt)Kunden
als Threads anlegen; Herr Fritz
und Frau Annerose
wären dann Instanzen dieser Klasse. Um den Einkauf tätigen zu können,
greifen beide Kunden auf Objekte der Klasse Einkaufswagen, z.B. wagen1,
wagen2,
etc. zu. Damit es dabei zu keinen
Konflikten (Inkonstistenzen) kommt, Herr
Fritz
und Frau Annerose
also
nicht gleichzeitig auf den gleichen Einkaufswagen z.B.
wagen1
zugreifen, müssen wir diese Objekte vor gleichzeitigem Zugriff schützen.
Zur Lösung dieses Problems benutzen wir einen sog. Monitor. Ein Monitor 'überwacht' ein Objekt z.B.
wagen1
und stellt fest, dass ein anderes Objekt, nämlich
fritz
auf wagen1
zugreift. Den Monitor können wir nun dazu veranlassen dass er keinen
zweiten Zugriff auf wagen1, etwa durch
annerose zulässt, Es ist also
so, als besitze fritz
für eine gewisse Zeit
die Schlüsselgewalt über wagen1,
der dann für den Zugriff anderer Objekte gesperrt ist (Lockin). Der wagen1
wird erst wieder frei, wenn fritz
seinen Einkauf beendet und den Wagen zurückgebracht hat. Er gibt den
Schlüssel zurück (Schloss: lock). Ein anderes Objekt kann die
Schlüsselgewalt übernehmen. Der Begriff des Monitors geht auf C.A.R.Hoare zurück, der dieses Konzept zum ersten mal 1978 in seinem
Aufsatz "Communicating Sequencential Processes" beschrieben hat. |
|
Locking in Java |
In Java können wir das Monitor-Konzept
auf zwei Weisen realisieren. Beide Fälle werden mit Hilfe des
Java-Schlüsselwortes
synchronized
realisiert
Abgewiesene Threads werden in eine Art 'Warteschleife' gespeichert.
Hat die Methode des zugelassenen Threads 'ihren Job' erfüllt, der Thread gibt seinen 'Schlüssel
wieder ab' und die Sperre ist wieder aufgehoben, kommt der nächste Thread aus der Warteschlange an die Reihe. Die
Reihenfolge kann man dadurch beeinflussen, dass man einzelnen Threads unterschiedliche Prioritäten gibt. |
|
Download: Monitor0.java |
|
|
Bemerkungen |
Monitor0.java ist den
Threadsprogrammen der letzten Unterkapitel nachempfunden. Die Klasse
Monitor0
erbt von Threads,
Den Standartkonstruktor ersetzen wir durch einen, der es erlaubt, der
Instanz von Monitor0
auch einen Namen zugeben. Wir überschreiben wie gewohnt die
run()-Methode,
die Monitor0
von Thread
geerbt hat. Sie ruft die statische Methode
schlafen()
auf, die wie gewohnt implementiert ist. Schließlich werden in der
main(..)-Methode
zwei Instanzen t1
und t2
dieser Klasse erzeugt und, sie sind ja Threads, gestartet. Das Ergebnis,
wie es unten dargestellt ist, ist uns bekannt. |
|
Ausgabe | Thread t1:
: 1 Thread t1: : 2 Thread t1: : 3 Thread t1: : 4 Thread t2: : 1 Thread t2: : 2 Thread t2: : 3 Thread t1: : 5 Thread t1: : 6 Thread t1: : 7 |
Thread t2:
: 4 Thread t1: : 8 Thread t1: : 9 Thread t2: : 5 Thread t1: : 10 Thread t2: : 6 Thread t2: : 7 Thread t2: : 8 Thread t2: : 9 Thread t2: : 10 |
Veränderung |
Wir verändern nun unser Programm und
wenden
synchronized
auf einen Block an. Die Methode getClass() aus der Klasse Object
liefert das Objekt, das
run() gerade aufruft und durch
eine mit
synchronized
statisch Methode (hier
schlafen()) gesperrt
wird. Die Veränderung ist im Quelltext gelb unterlegt.
|
|
Download Monitor1.java |
|
|
Bemerkungen |
Der Thread
t2
versucht auf schlafen()
zuzugreifen, während diese noch 'für den Thread
t1
tätig ist'.
synchronized
verhindert dies und lässt den zweiten Thread warten, bis der erste seinen
Aufruf von schlafen()
beendet hat. Die Ausgabe belegt dies: Zuerst kommt
t1
voll zum Zuge (linke Spalte) danach erst
t2
(rechte Spalte) |
|
Ausgabe |
Thread t1: : 1 Thread t1: : 2 Thread t1: : 3 Thread t1: : 4 Thread t1: : 5 Thread t1: : 6 Thread t1: : 7 Thread t1: : 8 Thread t1: : 9 Thread t1: : 10 |
Thread t2: : 1 Thread t2: : 2 Thread t2: : 3 Thread t2: : 4 Thread t2: : 5 Thread t2: : 6 Thread t2: : 7 Thread t2: : 8 Thread t2: : 9 Thread t2: : 10 |
Alternative |
Alternativ können wir die ganze
Methode schlafen()
mit einem Monitor versehen und dadurch vor doppeltem Zugriff schützen |
|
Download: Monitor2.java |
|
|
Ausgabe |
Die Ausgabe ist die gleiche, wie in
Monitor1.java |
|
In der Regel wird man beim Programmieren die zweite Variante verwenden. Auf die erste greift man zu, wenn man den Aufruf einer Methode für einen zweiten Thread sperren will, die Methode aber in einer Klasse liegt, die man selbst nicht geschrieben und auf deren Quelltext man keinen Zugriff hat. | ||
zu | 24.6 Einkaufen - Ein Beispiel | |
zur Startseite | www.pohlig.de (C) MPohlig 2004 |