Initialization-on-demand holder idiom

In der Softwareentwicklung bezeichnet das Entwurfsmuster Initialization on Demand Holder eine Implementierungsmöglichkeit eines sogenannten Lazy-Initialisierten Singleton, also eine Implementierung, bei der das Objekt erst bei der ersten Verwendung initialisiert wird. In allen Java-Versionen erlaubt es eine sichere, hochgradig parallelisierbare Lazy-Initialisierung mit guter Performance.[1]

Folgendes Beispiel zeigt eine Implementierung des Idioms:

public class Something {
    private Something() {}

    private static class LazyHolder {
        private static final Something INSTANCE = new Something();
    }

    public static Something getInstance() {
        return LazyHolder.INSTANCE;
    }
}

Sie basiert auf der Initialisierungsphase der Ausführung innerhalb der Java Virtual Machine (JVM), wie sie in der Java Language Specification (JLS) beschrieben ist.[2] Wenn die Klasse Something durch die JVM geladen wird, geht sie durch den Initialisierungsprozess. Da die Klasse keine statischen Variablen zu initialisieren hat, wird der Initialisierungsprozess sofort abgeschlossen. Die statische Klassendefinition LazyHolder innerhalb der Klasse wird nicht initialisiert, solange die JVM nicht erkennt, dass die Klasse LazyHolder benötigt wird und ausgeführt werden muss. Die statische Klasse LazyHolder wird nur ausgeführt, wenn die statische Methode getInstance in der Klasse Something verwendet wird. Beim ersten Aufruf der Methode lädt und initialisiert die JVM die Klasse LazyHolder. Die Initialisierung der Klasse LazyHolder resultiert ebenso in der Initialisierung der statischen Variable INSTANCE welche den (privaten) Konstruktor der umschließenden Klasse Something aufruft. Da die Initialisierungsphase einer Klasse laut der JLS garantiert seriell, also nicht-parallel, abläuft, ist keine weitere Synchronisierung in der statischen Methode getInstance während des Ladens und der Initialisierung nötig. Da die Initialisierungsphase die statische Variable INSTANCE in einer seriellen Operation beschreibt geben alle parallelen Aufrufe der getInstance Methode dieselbe, korrekt initialisierte Variable INSTANCE zurück.

Dies ergibt einen hoch-effizienten, Thread-sicheren „singleton“-Cache, ohne Synchronisierungs-Mehraufwand; Benchmarks ergaben, dass diese Implementierung deutlich schneller ist als viele andere mit Synchronisierungen.[3] Nichtsdestotrotz ist dieses Idiom Singleton-spezifisch und nicht erweiterbar auf Mehr-Objekt-Klassen.

Nur eine Gelegenheit zur Initialisierung

[Bearbeiten | Quelltext bearbeiten]

Trotz der Eleganz dieses Ansatzes (welcher erstmals von Pugh beschrieben wurde) resultiert jeder Fehler während der Initialisierung der Klasse in einem nicht-nutzbaren Zustand der Holder-Klasse, was bedeutet, dass dieser Ansatz nur dann verwendet werden sollte, wenn sich der Entwickler sicher ist, dass die Initialisierung nicht fehlschlagen kann. Beispiel:

public class PughFail {
    public static class Something {
        private Something() {
            super();
            System.out.println(this.getClass().getName() + " called");
            if (System.currentTimeMillis() > 0) {
                System.out.println("EMULATING INIT FAILURE");
                throw new RuntimeException("EMULATING INIT FAILURE");
            }
        }
        private static class LazyHolder {
            private static final Something INSTANCE = new Something();
        }
        public static Something getInstance() {
            return LazyHolder.INSTANCE;
        }
    }
    public static void main(String[] args) {
        System.out.println("First try");
        try {
            Something.getInstance();
        } catch (Throwable t) {
            System.out.println(t);
        }
        System.out.println("Second try");
        try {
            Something.getInstance();
        } catch (Throwable t) {
            System.out.println(t);
        }
    }
}

Ausgabe:

First try
PughFail$Something called
EMULATING INIT FAILURE
java.lang.ExceptionInInitializerError
Second try
java.lang.NoClassDefFoundError: Could not initialize class PughFail$Something$LazyHolder

Einzelnachweise

[Bearbeiten | Quelltext bearbeiten]
  1. Das Double Checked Locking Idiom funktioniert nicht korrekt in Versionen vor Java 1.5.
  2. 12.4 of Java Language Specification.
  3. Fastest Thread-safe Singleton in the JVM. In: literatejava.com.