21 Jun, 2009 von Benjamin Erb
Das Singleton-Pattern in Java
Das Singleton-Pattern ist ein Erzeugungsmuster aus dem Klassiker Design Patterns der Gang of Four. Es beschreibt, wie man von einer Klasse gezielt nur eine einzige Instanz erzeugt und darauf einen einfachen Zugriff möglich ist. Typische Einsatzzwecke des Patterns sind zum Beispiel Logger, die von überall im Programm Ereignisse protokollieren sollen oder Manager-Klassen, wie dem Toolkit.getDefaultToolkit(), das bei java.awt eine betriebssystemspezifische Instanz zurückgibt. Das Pattern ist nicht ganz unumstritten, da es Ähnlichkeiten mit unliebsamen globalen Variablen aufweist und außerdem die Testbarkeit von Klassen erschwert, doch hierum soll es an dieser Stelle nicht gehen, sondern viel mehr darum, wie man dieses Pattern in Java implementiert.
Zunächst muss man sicherstellen, dass von der Klasse keine neuen Instanzen erzeugt werden können. Hierfür setzt man die Sichbarkeit des Konstruktors auf private. Die einzige Instanz wird über eine statische Factory-Methode übergeben. Nun gibt es zwei mögliche Zeitpunkte, die Singleton-Instanz zu erzeugen. Entweder beim ersten Aufruf der Factory-Methode (lazy creation), oder aber bereits bei der nitialisierung der Klasse (eager creation). Beide Varianten haben aber zunächst einen kleinen Nachteil.
Die lazy creation ist interessant, weil sie das Objekt wirklich erst dann erstellt, wenn es benötigt wird. Allerdings muss bei der Erstellung der Objektes sichergestellt werden, dass wirklich nur eine Instanz erzeugt wird. Versuchen zeitgleich mehrere Threads, die Instanz zu erzeugen, kann es zu mehreren Instanziierungen kommen. Hier gibt es nun zwei Lösungsansätze: Entweder wird die statische Factory-Methode als synchronized gesetzt, wodurch dann immer nur ein Thread dedizierten Zugriff auf die Instanz hat, was alle anderen Threads blockiert und somit sehr ineffizient ist bei längeren Zugriffen auf die Instanz.
//Explizit synchronisierte lazy declaration Variante - ineffizient! public class MyClass { /** Feld für Singleton-Instanz */ private static MyClass instance; /** * Privater Konstruktor verhindert externe Instanzierung */ private MyClass() { } /** * Statische, synchronisierte Factory-Methode * @return Singleton-Instanz */ public synchronized static MyClass getInstance() { if (instance == null) { instance = new MyClass(); } return instance; } }
Eine Alternative wäre das double-checked locking, was allerdings in Java durch das Speichermodell nicht sicher ist und dringend vermieden werden sollte.
Empfehlerswerter bei Java ist also die eager creation, die zum Beispiel so aussehen könnte:
//einfache eager creation Variante - evenutell unnoetige Instanziierung! public class MyClass { /** Feld für Singleton-Instanz */ private static final MyClass instance = new MyClass(); /** * Privater Konstruktor verhindert externe Instanzierung */ private MyClass() { } /** * Statische Factory-Methode * @return Singleton-Instanz */ public static MyClass getInstance() { return instance; } }
Durch einen kleinen Trick kann man verhindern, dass die Instanz schon beim Laden der Klasse erzeugt wird. Hierfür wird eine innere, statische Holder-Klasse geschrieben, die die Instanz kapselt. Diese Varinate ist außerdem implizit threadsicher und deswegen empfehlenswert:
//elegante eager creation Variante - implizite Threadsicherheit public class MyClass { /** * Privater Konstruktor verhindert externe Instanzierung */ private MyClass() { } /** * Innere statische Holder-Klasse */ private static class Holder { /** Gekapselte Instanz */ private static final MyClass INSTANCE = new MyClass(); } /* * Statische Factory-Methode * @return Singleton-Instanz */ public static MyClass getInstance() { return Holder.INSTANCE; } }
Seit Java 5 besteht noch eine alternative Variante, die sich die Eigenschaften des Enum-Konstruktes zu eigen macht. Anders als bei C/C++ besitzen die Enum-Typen bei Java objektähnliche Eigenschaften. Somit lässt sich ein Enum als Singleton-Klasse missbrauchen. Dadurch verliert man zwar die Möglichkeit, davon weitere Klassen abzuleiten, dafür ist die Singleton-Instanz direkt serialisierbar.
//Variante mit Enum ab Java 5 public enum MyClass { /** Singleton-Instanz als Enum-Typ */ INSTANCE; /** * Konstruktor */ MyClass() { } }
Originalartikel: Das Singleton-Pattern in Java
Letzte Kommentare