Zuletzt aktualisiert am 16.12.2025 5 Minuten Lesezeit

Singleton

Singleton ist ein Entwurfsmuster (Design Pattern) aus der objektorientierten Programmierung. Es stellt sicher, dass von einer Klasse nur genau eine Instanz existiert und bietet einen globalen Zugriffspunkt auf diese Instanz. Der Name leitet sich vom englischen Begriff fuer "Einzelstueck" ab.

Das Singleton-Pattern gehoert zu den sogenannten Erzeugungsmustern (Creational Patterns) und wurde durch das Buch "Design Patterns" der Gang of Four (GoF) populaer. Es loest das Problem, wenn genau eine Instanz eines Objekts benoetigt wird - etwa fuer Konfigurationen, Datenbankverbindungen oder Logger.

Funktionsweise des Singleton-Patterns

Ein Singleton besteht aus drei wesentlichen Elementen:

  1. Privater Konstruktor: Verhindert, dass andere Klassen mit new eine Instanz erzeugen koennen
  2. Statische Instanzvariable: Speichert die einzige Instanz der Klasse
  3. Statische Zugriffsmethode: Liefert die Instanz zurueck (meist getInstance() genannt)

Die Grundidee ist simpel: Da der Konstruktor privat ist, kann niemand von aussen ein Objekt erzeugen. Die einzige Moeglichkeit, an eine Instanz zu gelangen, fuehrt ueber die statische getInstance()-Methode. Diese prueft, ob bereits eine Instanz existiert - falls nicht, wird sie erzeugt und gespeichert.

Implementierung in Java

Es gibt verschiedene Wege, ein Singleton zu implementieren. Jede Variante hat ihre Vor- und Nachteile bezueglich Einfachheit, Lazy Loading und Thread-Sicherheit.

Eager Initialization (Fruehe Initialisierung)

Die einfachste Variante erstellt die Instanz direkt bei der Klasseninitalisierung. Sie ist automatisch thread-sicher, da die JVM die Klassenladung synchronisiert.

public class Singleton {
    // Instanz wird sofort bei Klassenladung erzeugt
    private static final Singleton INSTANCE = new Singleton();
    
    // Privater Konstruktor - von aussen nicht aufrufbar
    private Singleton() {}
    
    // Globaler Zugriffspunkt
    public static Singleton getInstance() {
        return INSTANCE;
    }
    
    // Beispiel-Methode
    public void doSomething() {
        System.out.println("Singleton arbeitet...");
    }
}

Der Nachteil: Die Instanz wird erzeugt, auch wenn sie nie benoetigt wird. Bei ressourcenintensiven Objekten kann das verschwenderisch sein.

Lazy Initialization (Spaete Initialisierung)

Bei der Lazy Initialization wird die Instanz erst beim ersten Aufruf von getInstance() erzeugt. Diese Variante ist allerdings nicht thread-sicher:

public class Singleton {
    private static Singleton instance;
    
    private Singleton() {}
    
    // ACHTUNG: Nicht thread-sicher!
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

Das Problem: Wenn zwei Threads gleichzeitig getInstance() aufrufen, koennten beide feststellen, dass instance null ist, und jeweils eine eigene Instanz erzeugen. Das ist eine klassische Race Condition.

Thread-sichere Varianten

Um die Race Condition zu vermeiden, gibt es mehrere Ansaetze:

Synchronized Method

public static synchronized Singleton getInstance() {
    if (instance == null) {
        instance = new Singleton();
    }
    return instance;
}

Einfach, aber langsam: Jeder Aufruf muss synchronisiert werden, obwohl die Synchronisation nur beim ersten Aufruf noetig waere.

Double-Checked Locking

public class Singleton {
    private static volatile Singleton instance;
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (instance == null) {                    // Erste Pruefung
            synchronized (Singleton.class) {
                if (instance == null) {            // Zweite Pruefung
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

Durch die doppelte Pruefung (double-checked) wird die Synchronisation nur beim ersten Aufruf benoetigt. Das volatile-Schluesselwort ist wichtig, um sicherzustellen, dass alle Threads die korrekte Instanz sehen.

Bill Pugh Singleton (Initialization-on-demand holder)

Diese elegante Loesung nutzt die Eigenschaften des Java-Classloaders:

public class Singleton {
    private Singleton() {}
    
    // Innere Klasse wird erst bei Zugriff geladen
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

Die innere Klasse SingletonHolder wird erst geladen, wenn getInstance() aufgerufen wird. Die JVM garantiert, dass die Klasseninitialisierung atomar erfolgt - damit ist diese Variante sowohl lazy als auch thread-sicher, ohne explizite Synchronisation.

Enum Singleton (Empfohlen)

Die einfachste und robusteste Loesung in Java ist die Verwendung eines Enums:

public enum Singleton {
    INSTANCE;
    
    public void doSomething() {
        System.out.println("Singleton arbeitet...");
    }
}

// Verwendung:
Singleton.INSTANCE.doSomething();

Enums sind von Natur aus thread-sicher und serialisierbar. Zusaetzlich sind sie gegen Reflection-Angriffe geschuetzt, mit denen man bei anderen Varianten den privaten Konstruktor umgehen koennte. Joshua Bloch, Autor von "Effective Java", empfiehlt diese Variante als beste Loesung.

Typische Anwendungsfaelle

Das Singleton-Pattern eignet sich fuer Situationen, in denen genau eine zentrale Instanz sinnvoll ist:

  • Logger: Eine zentrale Logging-Instanz fuer die gesamte Anwendung
  • Konfiguration: Einmalig geladene Einstellungen, die ueberall verfuegbar sein sollen
  • Datenbankverbindungen: Connection Pools mit einer zentralen Verwaltung
  • Cache: Ein globaler Cache fuer haeufig benoetigte Daten
  • Hardware-Zugriff: Drucker-Spooler, Geraetetreiber mit exklusivem Zugriff
  • Thread Pools: Zentrale Verwaltung von Worker-Threads

Vor- und Nachteile

Vorteile Nachteile
Garantiert eine einzige Instanz Verletzt das Single Responsibility Principle
Globaler Zugriffspunkt Erschwert Unit-Tests (schwer zu mocken)
Lazy Initialization moeglich Versteckte Abhaengigkeiten im Code
Kontrollierter Zugriff auf Ressourcen Globaler Zustand kann zu Problemen fuehren
Thread-sicher implementierbar Oft als Anti-Pattern betrachtet

Kritik am Singleton-Pattern

Das Singleton-Pattern ist eines der umstrittensten Design Patterns. Viele erfahrene Entwickler betrachten es als Anti-Pattern - ein Muster, das mehr Probleme schafft als es loest.

Testbarkeit

Das groesste Problem: Singletons sind schwer zu testen. In Unit-Tests moechte man Abhaengigkeiten durch Mock-Objekte ersetzen. Bei einem Singleton ist das ohne spezielle Frameworks kaum moeglich. Der globale Zustand kann ausserdem dazu fuehren, dass Tests sich gegenseitig beeinflussen.

Versteckte Abhaengigkeiten

Wenn eine Klasse Singleton.getInstance() aufruft, ist diese Abhaengigkeit nicht im Konstruktor oder in den Methodenparametern sichtbar. Das macht den Code schwerer zu verstehen und zu warten.

Verletzung des Single Responsibility Principle

Eine Singleton-Klasse hat zwei Verantwortlichkeiten: ihre eigentliche Aufgabe und die Kontrolle ihrer Instanziierung. Das widerspricht dem Single Responsibility Principle aus den SOLID-Prinzipien.

Alternativen zum Singleton

Moderne Softwarearchitekturen bieten bessere Alternativen:

  • Dependency Injection (DI): Ein DI-Container verwaltet die Instanzen und injiziert sie dort, wo sie benoetigt werden. Die Klasse selbst weiss nichts von ihrer "Einzigartigkeit"
  • Factory Pattern: Eine Factory kontrolliert die Objekterzeugung und kann bei Bedarf immer dieselbe Instanz zurueckgeben
  • Monostate Pattern: Alle Instanzen einer Klasse teilen sich denselben Zustand ueber statische Variablen
  • Service Locator: Ein zentrales Register fuer Services (aehnlich problematisch wie Singleton)

In modernen Frameworks wie Spring (Java), ASP.NET Core (C#) oder NestJS (TypeScript) wird Dependency Injection standardmaessig verwendet. Dort kannst du Services als "Singleton" registrieren, ohne das Pattern selbst zu implementieren.

Singleton in der IT-Ausbildung

Fuer Fachinformatiker in der Anwendungsentwicklung ist das Singleton-Pattern ein wichtiges Thema. Es wird haeufig in Pruefungen behandelt und zeigt grundlegende Konzepte wie private Konstruktoren, statische Methoden und Thread-Sicherheit.

Wichtig ist, nicht nur die Implementierung zu kennen, sondern auch die Kritikpunkte zu verstehen. In Vorstellungsgespraechen und Pruefungen wird oft gefragt, wann man ein Singleton verwenden sollte - und wann besser nicht.

Quellen und weiterfuehrende Links