![]() | |||||||||
|
Das Entwurfsmuster Singleton testenDer folgende Text ist ein Auszug aus dem Buch Das Entwurfsmuster Singleton ist der populärste Vertreter der Erzeugungsmuster. Es wird immer dann eingesetzt, wenn von einem Objekt genau eine Instanz existieren soll. Eine Abwandlung ist das Fewton, das, wörtlich übersetzt, einige wenige Instanzen zulässt. Eine typische Implementierung des Singleton ist im folgenden Listing abgebildet, Zeilennummern sind zur besseren Orientierung angegeben. 0 public class SingletonClass
{ Die typischen Merkmale einer traditionellen Singleton-Klasse sind:
Anstatt also zu schreiben SingletonClass myObj = new SingletonClass(); wird das Singleton durch folgende Anweisung angewandt: SingletonClass myObj = SingletonClass.getInstance(); Weil die Methode getInstance() statisch ist, kann sie jederzeit aufgerufen werden. Das ist auch notwendig, weil ja gerade so eine (beim ersten Aufruf nicht vorhandene) Objektinstanz erzeugt werden soll und weil der Konstruktor privat ist. Das Singleton testen Ein Singleton-Test auf Anwendungsebene gestaltet sich recht einfach: public void testSingleton() { Tools wie PMD prüfen im Ansatz, ob für eine Klasse das Singleton angebracht wäre. PMD etwa schlägt vor, aus einer (konkreten) Klasse ein Singleton zu machen, wenn diese nur statische Methoden enthält. Thread-sichere Singleton-Version testen Die wohl am weitesten verbreitete, einfachste Implementierung der Singleton-Methode getInstance() ist nicht Thread-sicher. Das bedeutet, es kann zu Problemen beim parallelen Zugriff zweier Objekte auf dasselbe Singleton kommen. Bezogen auf das Listing 1 ergibt sich folgende Problematik: Thread 1 ruft zuerst getInstance() auf. Die Abarbeitung befinde sich nun in Zeile 5 (denn die Variable instance ist anfangs immer null), die aber noch nicht ausgeführt wurde. Thread 2 ruft ebenfalls die Methode auf. In Zeile 4 ist instance für Thread 2 noch mit null belegt. Daher wird auch für Thread 2 Zeile 5 ausgeführt. SingletonClass wird also zweimal instantiiert. Das widerspricht jedoch dem Charakter eines Singletons. Ein Singleton soll schließlich garantieren, dass immer höchstens eine Instanz der Singleton-Klasse existiert. Das Thema Thread-Sicherheit kann beliebig komplex werden, es sei nur an die Möglichkeit erinnert, mit mehreren virtuellen Maschinen zu arbeiten statt mit nur einer. Daher soll hier nur eine Idee vermittelt werden. Der Abschnitt Threads testen im Kapitel 5 des Buchs enthält weitere Informationen zum Thema. Der Test, ob ein Singleton Thread-sicher ist, ist nicht ganz einfach. Denn hierzu müssen mindestens zwei Threads ein Singleton aufrufen und der zweite Thread muss den ersten an einer ganz bestimmten Stelle überholen. Diese Stelle befindet sich nach Zeile 4 und vor Zeile 5 in Listing 1. Zunächst der Test einer Singleton-Implementierung hin auf Thread-Sicherheit: import
junit.framework.TestCase; Der obige Test wird so gut wie immer erfolgreich durchlaufen. Um den Test scheitern zu sehen, ist die Implementierung von getInstance() in der Singleton-Klasse geschickt anzupassen: Anstatt der Anweisung instance = new SingletonClass(); in
Zeile 5 von Listing 1 kann etwa folgendes geschrieben werden: instance = createSingletonClass(); und als Ergänzung der Klasse SingletonClass: protected SingletonClass
createSingletonClass() { Nun bringt dies nicht viel, ausser SingletonClass wird für den Test abgeleitet und in der abgeleiteten Klasse wird vor der obigen return-Anweisung folgendes eingesetzt: Thread.currentThread().sleep(50); Jetzt schlägt der oben angegebene Test zur Thread-Sicherheit fehl! Die Thread-sichere Variante des Singletons ist überraschend einfach zu implementieren, etwa so:
Die Variante umgeht die Schwierigkeiten, die mit dem Double-Checked Locking, dem synchronization-Keyword und anderen Spielarten verbunden sind. Anstatt die bekannte Methode getInstance() zu verwenden, greifen Klienten auf die vorinitialisierte statische Variable namens INSTANCE zu. Der Nachteil dieser einfachen Lösung ist deren Inflexibilität. Im Gegensatz zu komplexeren Lösungen, die eher anfällig gegen Zugriffskonflikte sind, kann diese Variante nicht an andere Bedürfnisse angepasst werden. Eine gute Ausgangsbasis für die Realisierung komplexerer, Thread-sicherer Singleton-Varianten sind die Queue-Klassen im Package java.util.concurrent (Java 5) bzw. edu.emory.mathcs.backport.java.util.concurrent (Java 1.4 Backport). |
||||||||
|