Informatik · Klasse 12 · aktualisiert
Monitor in der Informatik: Synchronisation verstehen
Du lernst, wie ein Monitor gemeinsame Daten schützt, wie wait() und signal() Threads koordinieren und wie Java das mit synchronized umsetzt.
0 von 9 Aufgaben gelöst
Kurz gesagt
Ein Monitor ist ein Modul, das gemeinsame Daten und ihre Zugriffsmethoden bündelt. Seine Monitor-Operationen laufen nur nacheinander. Mit wait() und signal() warten Threads auf Bedingungen. Java setzt das mit synchronized, wait(), notify() und notifyAll() um.
Zwei Threads ändern gleichzeitig denselben Kontostand, und plötzlich stimmt das Geld nicht mehr. Solche Fehler sind schwer zu finden, weil sie nur manchmal auftreten. Ein Monitor verhindert sie, ohne dass du jede Sperre selbst von Hand programmieren musst. Hier lernst du, wie das funktioniert.
Deine Lernziele
Hake ab, was du schon kannst. Löst du alle Aufgaben eines Abschnitts, hakt Mela das Ziel für dich ab.
4 Abschnitte
Ein Thread ist ein Ablauf innerhalb eines Programms. Mehrere Threads können dieselben Daten benutzen. Ein kritischer Abschnitt ist eine Ansammlung von Programmanweisungen, in der sich zu einer Zeit nur ein einziger Thread aufhalten darf. Er besteht aus mehreren Einzelanweisungen. Deren Zwischenergebnisse sind inkonsistente Zustände, auf die andere Threads keinen Zugriff erhalten dürfen. Nach außen darf das Ergebnis nur als eine unteilbare Einheit sichtbar werden.
Wenn sich ein Thread in einem kritischen Abschnitt bezüglich eines Betriebsmittels befindet, darf kein anderer Thread in einen kritischen Abschnitt bezüglich desselben Betriebsmittels gelangen. Das nennt man wechselseitigen Ausschluss. Eine strenge Reihenfolge der Nutzung ist dafür nicht nötig.
Ein Monitor ist ein Modul (zum Beispiel eine Klasse), in dem die gemeinsam genutzten Daten und ihre Zugriffsprozeduren zu einer Einheit zusammengeführt sind. Zugriffsprozeduren mit kritischen Abschnitten werden als Monitor-Operationen gekennzeichnet. Sie laufen unter wechselseitigem Ausschluss, ohne dass im Programmcode Synchronisationsanweisungen stehen müssen. Ruft ein Prozess A eine Monitor-Operation auf, während diese oder eine andere Monitor-Operation schon von einem Prozess B ausgeführt wird, wird A blockiert. Man stellt sich den Monitor oft als Raum vor, in dem nur ein Akteur Platz findet.
Definition
Monitor
Ein Monitor ist ein programmiersprachliches Konzept zur Synchronisation von Zugriffen mehrerer Prozesse oder Threads auf gemeinsam genutzte Datenstrukturen oder Ressourcen. Ein Compiler fügt dafür die nötigen Synchronisationsprimitive ein.
Beispiel: Ohne Schutz geht ein Zähler falsch (Annahme: zaehler startet bei 0)
- 1Annahme: Lesen und Schreiben von zaehler sind getrennte Schritte.
- 2Thread X liest zaehler (Wert 0). Thread Y liest zaehler ebenfalls (Wert 0).
- 3Beide erhöhen ihre private Kopie auf 1.
- 4X schreibt 1 zurück, danach schreibt Y ebenfalls 1.
- 5Endergebnis: 1 statt 2. Mit wechselseitigem Ausschluss läuft erst ein Thread komplett durch, dann der andere.
Merke: Ein Monitor sorgt dafür, dass seine Monitor-Operationen immer nur von einem Prozess zur Zeit ausgeführt werden.
Teste dich
Manchmal stellt ein Prozess in einer Monitor-Operation fest, dass die Daten gerade in einem Zustand sind, in dem er nicht sinnvoll weitermachen kann. Ohne weitere Mittel müsste er die Operation beenden, den Monitor verlassen und später erneut anfragen. Das führt zu unerwünschtem mehrfachem Prüfen und Warten.
Deshalb gibt es Bedingungsvariablen. Sie stehen für Bedingungen, die erfüllt sind oder nicht. Auf einer Bedingungsvariablen b gibt es zwei Operationen: wait() und signal(). Ruft ein Prozess in einer Monitor-Operation wait() an b auf, wird er blockiert und außerhalb des Monitors in eine Warteschlange zu b eingefügt. Jetzt ist kein Prozess mehr im Monitor aktiv, ein anderer Prozess kann den Monitor von außen betreten.
Ruft ein anderer Prozess in einer Monitor-Operation signal() an b auf, wird der blockierte Prozess deblockiert. Wartet zum Zeitpunkt des Aufrufs kein Prozess an b, ist der Aufruf ohne Auswirkung. Ein Monitor hat oftmals eine Monitor-Warteschlange, und jede Bedingungsvariable hat eine eigene Warteschlange.
Definition
Bedingungsvariable
Eine Bedingungsvariable repräsentiert eine Bedingung im Monitor. Auf ihr sind die Operationen wait() und signal() definiert.
Beispiel: Verbraucher wartet auf einen Wert (ausgedachtes Beispiel)
- 1Ausgedacht: Bedingungsvariable b steht für „Speicher ist nicht leer“.
- 2Verbraucher V findet den Speicher leer und ruft wait() an b auf.
- 3V wird blockiert und kommt außerhalb des Monitors in die Warteschlange zu b.
- 4Erzeuger E betritt den Monitor, legt einen Wert ab und ruft signal() an b auf.
- 5V wird deblockiert. Wer danach zuerst weiterläuft, hängt vom Monitor-Typ ab.
| Aufruf | Wer ruft auf? | Folge |
|---|---|---|
| wait() an b | Prozess in einer Monitor-Operation | Prozess wird blockiert, wartet in der Schlange zu b, Monitor wird frei |
| signal() an b | anderer Prozess in einer Monitor-Operation | wartender Prozess wird deblockiert |
| signal() an b | niemand wartet an b | Aufruf bleibt ohne Auswirkung |
Teste dich
Nach einem signal() gibt es ein Problem. Der deblockierte Prozess könnte an der Stelle weitermachen, an der er wait() aufgerufen hat. Aber der signalisierende Prozess ist ja noch im Monitor. Dafür wurden zwei Formen entwickelt.
Beim Hoare-Typ prüft signal zuerst, ob die Warteschlange der Bedingungsvariablen Prozesse enthält. Ist sie nicht leer, wird der signalisierende Prozess blockiert und in die Monitor-Warteschlange eingetragen. Ein Prozess aus der Warteschlange der Bedingungsvariablen wird deblockiert. Der signalisierende Prozess wird in der Regel erst fortgesetzt, nachdem der deblockierte Prozess den Monitor verlassen hat. Man nennt das auch Signal and Wait.
Beim Mesa-Typ blockiert signal den signalisierenden Prozess nicht. Er wird stets fortgesetzt. signal reiht stattdessen einen Prozess von der Warteschlange der Bedingungsvariablen in die Monitor-Warteschlange um. Man nennt das auch Signal-and-Continue (etwa: signalisieren und fortfahren).
| Typ | Signalisierender Prozess | Geweckter Prozess |
|---|---|---|
| Hoare (Signal and Wait) | wird blockiert, in der Regel erst später fortgesetzt | wird deblockiert |
| Mesa (Signal and Continue) | wird stets fortgesetzt | wird in die Monitor-Warteschlange umgereiht |
Merke: Die Frage lautet: Wer läuft nach signal() weiter? Beim Hoare-Typ in der Regel zuerst der Geweckte, beim Mesa-Typ der Signalisierende.
Teste dich
In Java hat prinzipiell jedes Objekt Monitorfähigkeiten. Methoden, die kritische Abschnitte auf Attributen enthalten, kennzeichnest du mit dem Schlüsselwort synchronized als Monitor-Operationen. Jedes Objekt der Klasse ist dann der Monitor für seine Attribute. Rufen mehrere Threads synchronized-Methoden am selben Objekt auf, läuft zu jedem Zeitpunkt höchstens ein Thread in einer solchen Methode. Der Monitor hat einen Monitor-Lock (Sperre) und eine Warteliste für ausgesperrte Threads. Endet eine synchronized-Methode oder ein synchronized-Block, wird die Sperre aufgehoben und der nächste Thread kommt zum Zug.
Java sieht keine Bedingungsvariablen vor. Für die Bedingungssynchronisation gibt es in der Klasse Object diese Methoden: wait() blockiert den aufrufenden Thread und gibt den Monitor frei. notify() entblockiert irgendeinen an diesem Monitor blockierten Thread, der weiterlaufen kann, sobald der Monitor frei ist. Dieses Verhalten ist nicht „fair“. notifyAll() entblockiert alle an diesem Monitor blockierten Threads.
Wegen der fehlenden Bedingungsvariablen muss ein deblockierter Thread seine Bedingung erneut prüfen. Sie könnte noch nicht gültig sein oder schon durch schnellere Threads wieder ungültig gemacht worden sein. Als Pseudocode (kein Java) sieht das so aus: solange ( nicht bedingung ) wait(). Das setzt voraus, dass beim Ändern der Variablen, auf die sich die Bedingung bezieht, stets notify aufgerufen wird.
Definition
synchronized
Schlüsselwort in Java. Es kennzeichnet Methoden oder Blöcke mit kritischen Abschnitten. Sie werden von mehreren Threads am selben Objekt nur nacheinander ausgeführt.
Beispiel: Ein synchronized-Block
- 1synchronized(obj) {
- 2 int a = obj.w;
- 3 a = a + 1;
- 4 obj.w = a;
- 5}
- 6Der Block ist ein kritischer Abschnitt. Jeder andere Thread, der ebenfalls synchronized(obj) aufruft, muss bis zum Ende des Blocks warten.
Merke: Ein geweckter Thread prüft seine Bedingung erneut, weil sie noch nicht gelten oder schon wieder ungültig sein könnte.
Teste dich
Alles auf einen Blick
Monitor (Informatik)
Kritischer Abschnitt
Dort darf sich zu einer Zeit nur ein Thread aufhalten.
Monitor
Er bündelt Daten und Monitor-Operationen und führt diese unter wechselseitigem Ausschluss aus.
wait() und signal()
wait() blockiert außerhalb des Monitors, signal() deblockiert einen Wartenden.
Hoare und Mesa
Entscheidend ist, wer nach signal() weiterläuft.
Java
synchronized, wait(), notify() und notifyAll() setzen das um; der Geweckte prüft seine Bedingung erneut.
Musteraufgabe · Schritt für Schritt
Kontostand ohne Schutz und mit Schutz
- 1Aufgabe: Das Konto hat 100 €. Person 1 zahlt 50 € ein, Person 2 hebt 100 € ab. Beide arbeiten zeitlich verschränkt ohne Schutz.
- 2Person 1 liest den Kontostand: 100 €.
- 3Person 2 liest 100 €, hebt 100 € ab und schreibt 100 € − 100 € = 0 €.
- 4Person 1 schreibt ihren Wert: 100 € + 50 € = 150 €.
- 5Der Stand 150 € ist falsch, die Abhebung ging verloren. Richtig wäre 100 € − 100 € + 50 € = 50 €.
- 6Lesen, Rechnen und Schreiben bilden zusammen den kritischen Abschnitt. Mit wechselseitigem Ausschluss läuft er bei einer Person komplett durch, bevor die andere beginnt.
Fehler finden
Prüfe die Aussagen zu Java und dem Monitor In einer Zeile steckt ein Fehler. Tippe sie an.
Fehler in Zeile 4Das ist ein Fehlschluss: Java hat keine Bedingungsvariablen. Richtig: Der geweckte Thread muss seine Bedingung erneut prüfen, denn sie könnte noch nicht gelten oder schon von schnelleren Threads ungültig gemacht worden sein.
Lückentext
Wähl in jeder Lücke das passende Wort und prüf dann deine Antworten.
Ruft ein Prozess wait() auf, wird er blockiert und wartet des Monitors in einer Warteschlange. Beim blockiert signal den signalisierenden Prozess nicht. In Java entblockiert irgendeinen an diesem Monitor blockierten Thread.
Karteikasten
Erst selbst überlegen, dann umdrehen.
Übung mit Feedback