Entwurfsmuster: Erzeugungsmuster Singleton

Das Singleton Entwurfsmuster hat dafür Sorge zu tragen, das die Entwicklungsumgebung höchstens eine einzige Instanz einer Klasse während der Laufzeit einer Anwendung erzeugt. Dies wird in der Regel unter anderem durch einen globalen Zugriffspunkt auf diese Instanz erreicht.

Als Voraussetzung für die einmalige Instanziierung einer Klasse sind einige Designmerkmale bei der Erstellung zu beachten:

  • privater parameterloser Konstruktor
  • Verwendung eines statischen Attributs – meist instance genannt
  • Zugriff auf dieses Attribut über eine öffentliche statische Methode – meist getInstance() genannt

Das folgende UML-Diagramm veranschaulicht ein Beispiel einer MyFirstSingleton Klasse.

Ein oft verwendeter Ansatz einer Singleton Implementierung ist folgender:

public class MyFirstSingleton 
{
	// statisches Attribut
	private static MyFirstSingleton instance;
	
	// private paramterloser Kontsruktor
	private MyFirstSingleton() {}
	
	public static MyFirstSingleton getInstance() 
	{
		if(instance == null)
		{
			instance = new MyFirstSingleton();
		}
		
		return instance;
	}
	
	public void method1(){}
	public void method2(){}
	public void method3(){}
}

In einer Singlethreading Umgebung ist die oben aufgeführte Variante zum Erstellen eines Singleton durchaus akzeptabel. Problematisch wird es jedoch, wenn die Instanziierung des Singleton Objekts in einer Multithreadumgebung aufgerufen wird. Greifen mehrere Threads nahezu gleichzeitig auf die Methode getInstance(), erhält jeder einzelne Thread seine eigene Instanz (Voraussetzung hierzu ist natürlich, das es sich um den ersten Zugriff auf die Methode getInstance() handelt, in der das Objekt initalisiert wird).
Abhilfe könnte das Schlüsselwort synchronized bei dem Methodenaufruf getInstance() schaffen. Synchronized veranlasst bekanntlich, das immer nur ein Thread gleichzeitig Zugriff auf diese Methode hat.

public static synchronized MyFirstSingleton getInstance()
{ /* ... */ }

Grundsätzlich würde diese Realisierung das Problem in einer Multithreadingumgebung lösen. Es ist jedoch zu beachten, das lediglich bei der Initialisierung ein synchronized erforderlich ist, weshalb diese Lösung zu späteren Zeitpunkt eher eine Blockade ist, da Multithreading ausgeschlossen wird.
Lösung des ganzen Problem ist die Umsetzung einer statischen Initialisierung des instance Attributs.

public class MyFirstSingleton
{
    // statisches Attribut
    private static final MyFirstSingleton INSTANCE = new MyFirstSingleton();
 
    // private paramterloser Kontsruktor
    private MyFirstSingleton() {}
 
    public static MyFirstSingleton getInstance()
    {
        return INSTANCE;
    }
 
    public void method1(){}
    public void method2(){}
    public void method3(){}
}

In dieser Implementierung wird das Objekt bei der ersten Benutzung der Klasse erstellt, womit jeglicher nachträglicher Synchronisierungsaufwand entfällt. Das Schlüsselwort final sorgt zusätzlich dafür, dass das Objekt unverändert bleibt womit letztendlich jede NULL Prüfung entfällt.

Hinweis: Unabhängig von der Umsetzung des Singleton Entwurfsmusters muss jedoch bedacht werden, das pro ClassLoader eine Objektinstanz existieren kann.

Heutzutage werden Singleton auch oft mit einem ENUM (erst ab JDK 5 verfügbar) gelöst.

public enum MyFirstEnumSingleton 
{
	INSTANCE;
	
	private int att1;
	private String att2;
	
	public void method1(){}
    public void method2(){}
    public void method3(){}
    
}

Die Verwendung von ENUM Singletons ist eindeutig und pro JVM genau einmal vorhanden.