Weitere aktuelle Java-Titel finden Sie bei dpunkt.
 Inhaltsverzeichnis   Auf Ebene Zurück   Seite Zurück   Seite Vor   Auf Ebene Vor   Eine Ebene höher   Index


11.7.2

Sperren



Im vorhergehenden Abschnitt wurde synchronized angewendet, ohne näher darauf einzugehen, was dahintersteckt. Der exklusive Zugriff mit synchronized ist mit Sperren (Locks) realisiert. Jedes Java-Objekt ist mit einer solchen Sperre assoziiert. Sobald eine synchronized-Methode betreten wird, besitzt der aufrufende Thread die Sperre des Objekts, dessen Methode aufgerufen wurde.

Da die Sperre während der gesamten Ausführung einer synchronized-Methode nicht mehr abgegeben wird, kann kein anderer Thread in dieser Zeit eine synchronized-Methode auf dem gesperrten Objekt ausführen. Dagegen kann der Thread, der das Sperrobjekt bereits besitzt, sehr wohl weitere geschützte Methoden aufrufen, weil Monitore in Java reentrant sind.

Falls die Sperre besetzt ist, werden andere Threads, die eine auf dieselbe Sperre synchronisierte Methode aufrufen, in eine Warteschlange gestellt. Wenn das Sperrobjekt wieder frei wird, kann der nächste Thread aus der Warteschlange den geschützten Bereich betreten. Hierbei können keine Annahmen über die Reihenfolge gemacht werden, in der den wartenden Threads die Sperre zugeteilt wird.

Um eine gute Systemauslastung zu erzielen, ist man stets bestrebt, die geschützten und somit sequenzialisierten Bereiche so klein wie möglich zu halten. Insbesondere dann, wenn die Zugangsmethoden im Vergleich zu den kritischen Bereichen recht lang sind, wäre eine feinere Sperrgranularität wünschenswert.

Zu diesem Zweck kann das Schlüsselwort synchronized auf eine zweite Art verwendet werden:
  public void aMethod() {
    // unsynchronisierte Anweisungen
    synchronized(anObject) {
      // kritische Region
    }
    // unsynchronisierte Anweisungen
  }
anObject ist hierbei das Sperrobjekt, mit dem synchronisiert wird. Mit dieser Syntax von synchronized kann ein Block durch ein beliebiges Objekt gesperrt werden. Da Arrays in Java ebenfalls Objekte sind, kann man so auch den Zugriff auf ein Array schützen.

Technisch gesehen ist eine synchronized-Exemplarmethode nichts anderes als ein großer synchronized-Block, der auf das zugehörige Exemplar synchronisiert ist. Das Schlüsselwort synchronized im Kopf einer Methode ist also äquivalent zum Einfassen des kompletten Rumpfs in einen synchronized(this)-Block.

Aus diesem Grund wird im Folgenden nur noch von synchronized-Blöcken gesprochen, was die synchronized-Methoden impliziert.

Bei synchronized-Blöcken gilt grundsätzlich:

Synchronisation auf mehrere Sperrobjekte

Falls eine kritische Region von mehreren Sperrobjekten geschützt werden muss, kann man die entsprechenden synchronized-Blöcke einfach schachteln:
  synchronized(lock1) {
    synchronized(lock2) {
      synchronized(lock3) {
        ...
      }
    }
  }
Bei der Verwendung mehrerer Sperrobjekte muss allerdings strikt beachtet werden, dass alle kritischen Regionen die Sperrobjekte in der gleichen Reihenfolge anfordern, da sonst schnell ein Deadlock eintreten kann, wie das folgende Beispiel zeigt:
  // Thread 1:
  synchronized(lock1) {
    synchronized(lock2) {
      ...
    }
  }

  // Thread 2:
  synchronized(lock2) {
    synchronized(lock1) {
      ...
    }
  }
Unter der Annahme, dass lock1 und lock2 zu Anfang zwei verschiedene Objekte referenzieren und nicht verändert werden, entsteht ein Deadlock, wenn Thread 1 das Objekt lock1 belegt und Thread 2 direkt danach lock2 in seinen Besitz nimmt.

Die Einhaltung einer bestimmten Reihenfolge beim Sperren wird auch hierarchische Betriebsmittelzuteilung genannt. Diese Technik garantiert Deadlock-Freiheit. Sie basiert darauf, dass eine Ordnung auf den Sperren definiert ist. Wenn ein Thread eine Sperre belegen will, darf er zu diesem Zeitpunkt keine Sperren mit einer größeren Ordnungszahl besitzen.

Im Beispiel verletzt Thread 2 dieses Prinzip, wodurch es zu einem Deadlock kommen kann.

Generell ist besondere Vorsicht geboten, wenn die Referenzen in den Argumenten von synchronized-Blöcken im Lauf des Programms geändert werden, da sich eine Sperre immer auf ein Objekt und nicht auf eine Referenz bezieht. Dass die Referenzen immer in der gleichen Reihenfolge angegeben sind, impliziert nicht unbedingt, dass die referenzierten Objekte gemäß der definierten Ordnung angefordert werden. Um sicherzustellen, dass die Referenzen auf Sperrobjekte nicht verändert werden, können sie als final deklariert werden.


 Inhaltsverzeichnis   Auf Ebene Zurück   Seite Zurück   Seite Vor   Auf Ebene Vor   Eine Ebene höher   Index

Copyright © 2002 dpunkt.Verlag, Heidelberg. Alle Rechte vorbehalten.