Kritische Abschnitte

Wenn zwei unabhängige Programme, Prozesse oder Threads auf gemeinsame Daten zugreifen, kann es zu Kollisionen kommen. Der Scheduler kann zu völlig unvorhersehbaren Zeitpunkten zwischen zwei (oder mehreren) Threads umschalten. Solange jeder Thread seine eigenen Variablen besitzt, ist dies kein Problem. Werden hingegen gemeinsame Variablen gelesen und vorallem geschrieben, muss der Zugriff geschützt werden. Ein Abschnitt in einem Programm, der anfällig auf Kollisionen ist und geschützt werden muss, nennt man kritischer Abschnitt.

Schon folgende einfache Anweisung kann ein kritischer Abschnitt sein: i = i + 1;

Dabei soll Variable i von zwei Threads A und B gemeinsam zugegriffen werden; zu Beginn sei i = 0. Es kann sich folgendes Szenario abspielen:

  1. Thread A berechnet i+1 (= 1) wird aber unterbrochen, bevor das Resultat wieder i zugewiesen werden kann.
  2. Thread B berechnet auch i+1 und schreibt das Resultat nach i zurück (i = 1). Der Scheduler schaltet wieder zu Thread A.
  3. Thread A schreibt nun sein Resultat nach i zurück (i = 1).

Variable i wurde also zweimal um 1 erhöht, das Ergebnis ist aber nur 1. Für jeden Thread einzeln gesehen stimmt dieses Resultat, also wo liegt das Problem?

Angenommen obige Anweisung stellt eine Einzahlung auf ein Konto dar. i sei Ihr Konto und 1 seien Fr. 100.- ! Zwei Einzahlungen à Fr. 100.- werden zu Ihren Gunsten an zwei verschiedenen Filialen einer Bank gleichzeitig gemacht. Die Bank kassiert Fr. 200.-, Sie haben aber nur Fr. 100.- auf dem Konto!

Wie wird ein kritischer Abschnitt geschützt?

In den 60'er Jahren wurde von E. W. Dijkstra das Konzept der Semaphoren entwickelt. Stark vereinfacht handelt es sich dabei um ein Konstrukt, das vor und nach dem kritischen Abschnitt plaziert wird, und verhindert, dass mehr als ein Thread gleichzeitig den kritischen Abschnitt bearbeitet. Dabei wird das Ein- und Austreten in bzw. aus einem kritischen Abschnitt markiert. Dies läuft ungefähr so ab:

Der erste Thread der in den kritischen Abschnitt kommt, markiert dies und arbeitet weiter. Kommt nun ein zweiter Thread auch zum Beginn desselben Abschnittes, sieht er die Markierung und wartet. Sobald der erste Thread fertig ist, löscht er die Markierung. Der zweite Thread markiert nun seinerseits das Eintreten in den kritischen Abschnitt und fährt mit dessen Bearbeitung fort. Man nennt dieses Vorgehen auch Synchronisation.

Eine genaue Beschreibung würde allerdings den Rahmen dieser Werkstatt sprengen und ist für das Bearbeiten dieses Postens auch nicht nötig. Genauere Informationen sind in [And91], [Dij65] und [Hoa75] erhältlich.

Welches sind kritische Abschnitte?

Wenn schon so einfache Anweisungen wie in unserem Beispiel gefährlich sind, könnte man dazu übergehen, gleich ganze Methoden zu synchronisieren. Dies mag sinnvoll sein, in vielen Fällen würde damit aber mit Kanonen auf Spatzen geschossen. Noch lange nicht jede Anweisung ist kritisch, und wegen ein oder zwei Anweisungen gleich eine ganze Methode zu schützen bringt nicht viel. Im Gegenteil: wird zu grosszügig geschützt, kann dies wiederum die Parallelität des Programmes stark beeinträchtigen. Denn im Prinzip heisst Synchronisieren auch Serialisieren! Achten Sie beim Programmieren auf folgende Punkte:

Wie Synchronisation in Java erreicht wird, ist auf der nächsten Seite beschrieben.


Kritische Abschnitte - Synchronisation in Java - Tips und Hinweise - Lösungsmöglichkeit
OOP
- Werkzeuge - Referenzen - Fäden - Synchronisation - Applets - Dokumentation
Werkstatt - Bibliographie