Muster: Java: Singleton



Singleton Bearbeiten

Erzeuge genau eine Instanz eines Objektes für diese Klasse eines Klassenladers (Sofern nicht durch Reflection erzeugt). Zur Sicherung einer Instanz in allen Klassenladern einer Java Virtual Maschine [JVM] bedarf es mehr Arbeit. (siehe [1] (vgl. WikiBook Muster)

Implementierung (1) Bearbeiten

package org.wikibooks.de.java.pattern;
public class Singleton {
 
  // Eine (versteckte) Klassenvariable vom Typ der eigenen Klasse
  private static Singleton instance;
  // Verhindere die Erzeugung des Objektes über andere Methoden
  private Singleton () {}
  // Eine Zugriffsmethode auf Klassenebene, welches dir '''einmal''' ein konkretes 
  // Objekt erzeugt und dieses zurückliefert.
  public static Singleton getInstance () {
    if (Singleton.instance == null) {
      Singleton.instance = new Singleton ();
    }
    return Singleton.instance;
  }
}

Achtung: Diese Implementation ist einfach und wird für die meisten Aufgaben auch reichen. Sie ist jedoch nicht Thread-sicher, da die Erzeugung kein atomarer Vorgang in der JVM ist.
-> Hier ist es möglich, dass mehrere Threads gleichzeitig diese Methode aufrufen. Im Falle von erforderlichen Initialisierungen im Konstruktor kann es vorkommen, dass ein Thread diese gerade ausführt und der andere schon die erstellte Instanz erhält, diese jedoch noch nicht komplett initialisiert ist. Solche Fehler treten meist erst im Realbetrieb unter Last auf und sind sehr schwer zu finden. Es sollte daher immer die synchronisierte Variante verwendet werden (siehe unten).
Nähere Infos siehe: [2]

Hier die Thread-sichere Version Bearbeiten

package org.wikibooks.de.java.pattern;
public class Singleton {
  
  // Eine (versteckte) Klassenvariable vom Typ der eigene Klasse
  private static Singleton instance;
  // Verhindere die Erzeugung des Objektes über andere Methoden
  private Singleton () {}
  // Eine Zugriffsmethode auf Klassenebene, welches dir '''einmal''' ein konkretes 
  // Objekt erzeugt und dieses zurückliefert.
  // Durch 'synchronized' wird sichergestellt dass diese Methode nur von einem Thread 
  // zu einer Zeit durchlaufen wird. Der nächste Thread erhält immer eine komplett 
  // initialisierte Instanz.
  public static synchronized Singleton getInstance () {
    if (Singleton.instance == null) {
      Singleton.instance = new Singleton ();
    }
    return Singleton.instance;
  }
}

Diese Version hat allerdings den Nachteil, dass jeder Zugriff auf die getInstance-Methode synchronisiert ist und dadurch bei sehr vielen Zugriffen zum potentiellen Flaschenhals wird.

Implizit synchronisierte Variante Bearbeiten

package org.wikibooks.de.java.pattern;
public class Singleton {
  
  // Innere private Klasse, die erst beim Zugriff durch die umgebende Klasse initialisiert wird
  private static final class InstanceHolder {
    // Die Initialisierung von Klassenvariablen geschieht nur einmal 
    // und wird vom ClassLoader implizit synchronisiert
    static final Singleton INSTANCE = new Singleton();
  }

  // Verhindere die Erzeugung des Objektes über andere Methoden
  private Singleton () {}
  // Eine nicht synchronisierte Zugriffsmethode auf Klassenebene.
  public static Singleton getInstance () {
    return InstanceHolder.INSTANCE;
  }
}

Das Initialisieren der Klassenvariablen wird vom ClassLoader implizit synchronisiert. Durch die Verwendung der inneren Klasse wird der Singleton-Konstruktor erst bei der Initialisierung der inneren Klasse, also in der getInstance-Methode, aufgerufen. Wäre die Variable INSTANCE eine Variable des Singletons, würde sie schon beim Zugriff auf die Klasse (z. B. Class.forName("org.wikibooks.de.java.pattern.Singleton")) initialisiert werden.