11.7.3 | Klassenbezogene Sperren |
Wenn man sicherstellen muss, dass der Zugriff auf eine kritische Region exklusiv gegenüber allen Threads ist, die eine bestimmte Monitor-Klasse verwenden, dann reicht ein synchronized(this) aufgrund der Überlegungen am Ende des letzten Abschnitts nicht aus, da die einzelnen Threads prinzipiell verschiedene Exemplare der Monitor-Klasse halten können.
Wenn ein Monitor mit einem Objekt synchronisiert sein soll, das definitiv das einzige Exemplar seiner Klasse ist, kann man ihn mit seiner Klasse oder präziser mit dem Class-Objekt seiner Klasse synchronisieren.
Das Class-Objekt einer Klasse ist stets einmalig in einer Virtual Machine. Insofern ist es auch gerechtfertigt, von »dem« Class-Objekt zu sprechen. Daher können mit diesem Objekt alle Threads einer Virtual Machine synchronisiert werden, auch dann, wenn sie unterschiedliche Exemplare der Monitor-Klassen halten oder wenn von der Monitor-Klasse gar keine Exemplare erzeugt werden können, weil sie auschließlich statische Zugangsmethoden definiert.
Solch eine klassenbezogene Sperre kann bei Applets nützlich sein, die im gleichen HTML-Dokument eingebunden sind.
Zunächst sind solche Applets zwar völlig getrennte Programme, da sie aber in derselben Virtual Machine im Browser laufen, können sie mit einem Class-Objekt synchronisiert werden. Im Internet Explorer (bis Version 5.x) und Netscape Navigator funktioniert diese Technik auch dann, wenn sich die Applets in verschiedenen Dokumenten vom gleichen Host befinden und in getrennten Fenstern angezeigt werden. Die Abbildung 11.3 zeigt ein Produzenten- und ein Konsumenten-Applet, die über ein Class-Objekt synchronisiert sind.
Ein weiterer Anwendungsfall für diese Technik wären Servlets, die eine gemeinsame Ressource (z. B. einen Seitenzugriffszähler) verwenden. Servlets werden wie Applets als parallele Threads in derselben Virtual Machine ausgeführt.
Vom grundsätzlichen Aufbau her stimmen die Zugangsmethoden mit denen des Beispiels im Abschnitt 11.7.4 überein. Der entscheidende Unterschied besteht darin, dass die Zugangsmethoden get() und put() als static vereinbart sind. Bei Klassenmethoden bewirkt der Modifier synchronized eine Synchronisierung auf das Class-Objekt der zugehörigen Klasse, im Gegensatz zu Exemplarmethoden, bei denen er gleichbedeutend mit synchronized(this) ist. Dadurch, dass die Zugangsmethoden als static vereinbart sind, wird also das gewünschte klassenbezogene Sperren erreicht.
Das Class-Objekt wird in beiden Methoden zunächst ermittelt, um dann über dieses Objekt die Synchronisation mittels wait() und notify() durchführen zu können:public class StaticMonitor { static int buffer; static boolean empty = true; public synchronized static void put(int data) { try { Class classLock = StaticMonitor.class; if (!empty) classLock.wait(); buffer = data; empty = false; classLock.notify(); } catch(InterruptedException e) { } } ... }Material zum Beispiel
Alternativ kann man auch in der Monitor-Klasse eine statische Referenz auf ein Sperrobjekt anlegen, das einmal beim Laden der Klasse erzeugt wird. Dadurch brauchen die Zugangsmethoden nicht mehr statisch zu sein, und die beteiligten Threads können die Zugangsmethoden auch über Exemplare der Monitor-Klasse aufrufen:
- Applet starten
- Quelltexte:
final static Object LOCK = new Object(); public void put(int number) { synchronized(LOCK) { try { if (!empty) LOCK.wait(); buffer = number; empty = false; LOCK.notify(); } catch(InterruptedException e) { } } }Material zum Beispiel
In der Standardbibliothek verwendet die Klasse java.awt.Component intern ein solches statisches Sperrobjekt, um Zeichenoperationen zu synchronisieren.
- Quelltexte: