Bob und Lisa betreiben zusammen eine Pizzaria. Während Bob in der Küche die Pizzen bäckt, bedient Lisa die Kunden im Restaurantbereich. Zwischen den Bereichen befindet sich in der Wand eine Öffnung mit einem kleinen Tresen. Immer wenn Bob eine Pizza fertiggebacken hat, legt er sie auf den Tresen. Lisa schaut immer wieder beim Tresen vorbei und sieht nach, ob Pizzen draufliegen, die sie abholen und an die Tische bringen kann.
Lisa schlägt vor, eine Tischglocke auf den Tresen zu stellen. Erklären Sie ihre Idee.
Das Erzeuger-Verbraucher-Problem tritt in der Informatik immer dann auf, wenn es Threads gibt, die Ressourcen erzeugen und in einem Ablagebereich mit beschränkter Kapazität ablegen während andere Threads, die Ressourcen aus dem Bereich entnehmen und verbrauchen.
Um passives Warten zu ermöglichen besitzt der Monitor die Methoden wait()
(versetzt den aktuellen Thread in den Wartezustand) und notify()
(holt einen der wartenden Threads aus dem Wartezustand).
Das zentrale Element des nachfolgenden Programms sind die Methoden legPizzaDrauf
und holPizzaAb
:
class Tresen { int anzahlPizzen = 0; synchronized void legPizzaDrauf() { while (anzahlPizzen == 1) { wait(); // aktueller Thread geht in den Wartezustand und blockiert } anzahlPizzen = 1; notify(); // beliebigen Thread aus dem Wartezustand "aufwecken" } synchronized void holPizzaAb() { while(anzahlPizzen == 0) { wait(); // aktueller Thread geht in den Wartezustand und blockiert } anzahlPizzen = 0; notify(); // beliebigen Thread aus dem Wartezustand "aufwecken" } }
In der folgenden Darstellung ist Bobs Thread blau gekennzeichnet, Lisas Thread grün. Die Stelle, an der sich der Thread jeweils befindet, ist mit einem Punkt gekennzeichnet. Der hellrot hinterlegte Bereich kennzeichnet das Tresen-Objekt mit seinen synchronized
-Methoden und seinem Zustand (anzahlPizzen
).
Stellen Sie sich mit Hilfe der Darstellung Bob und Alice vor, wie
wait()
), notify()
) anzahlPizzen
) verändert.In den obigen Erklärungen wurden ein paar Sachverhalte vereinfacht, damit Sie das Prinzip des passiven Wartens gut verstehen können. Vielleicht sind Ihnen daher ein paar kleinere Ungereimtheiten aufgefallen. Es wird Zeit, sie jetzt im Detail zu klären!
legPizzaDrauf
und holPizzaAb
auch einfach nur if-statements verwenden. Sobald es mehrere Erzeuger/Verbraucher gibt, könnte es aber sein, dass ein Erzeuger durch das notify()
eines anderen Erzeugers "aufgeweckt" wurde. In diesem Fall ist dann die while-loop
unverzichtbar, da der Erzeuger "aufgeweckt" wurde ohne dass ein freier Platz im Austauschbereich geschaffen worden war. notify()
durch den Erzeuger direkt den Verbraucher "aufweckt" (und umgekehrt), so dass letzterer sofort mit der Abarbeitung der Anweisung fortfährt, die auf die wait()
-Anweisung folgt. Aber dann wären ja sowohl Erzeuger als auch Verbraucher gleichzeitig in der synchronized-Methode! Das darf nicht sein, denn nach dem notify()
könnten ja ohne Weiteres noch weitere Anweisungen in der synchronized
-Methode stehen.runnable
in den Zustand running
versetzt und setzt seine Ausführung fort. waiting
und runnable
.
Erweitern Sie den obigen Algorithmus zum passiven Warten so, dass es 10 Producer und 10 Verbraucher gibt und die Kapazität des Tresens erhöht werden kann (z.B. auf 3 Pizzen).
Sie werden dazu die Aufrufe von notify()
durch notifyAll()
ersetzen müssen. Erklären Sie, weshalb dies nötig ist und welchen Nachteil es mit sich bringt.
Lösung