Das klassische Singleton garantiert die Erstellung nur einer Instanz eines Objekts. In Java gibt es mehrere Möglichkeiten der Implementierung, aber unter Berücksichtigung von Multithreading und Serialisierung müssen folgende Punkte beachtet werden:
public class Singleton { private static volatile Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
enum EnumSingleton { INSTANCE; // Methoden }
private Object readResolve() { return getInstance(); }
Reicht es aus, die Methode getInstance() synchronisiert zu gestalten, um ein thread-sicheres Singleton zu erreichen?
Antwort: Ja, aber dieser Ansatz führt zu einer Verringerung der Leistung, da synchronized bei jedem Zugriff auf getInstance aufgerufen wird. Effizienter ist das Double-checked Locking in Kombination mit volatile für die Instanz oder die Verwendung eines Enums zur Implementierung des Singletons.
Beispiel für ineffizienten Code:
public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }
Geschichte
Im Finanzsystem wurde bei der Implementierung des Singletons das
volatile-Keyword bei der Instanzreferenz in der Double-checked Locking-Implementierung vergessen. Infolgedessen entstanden bei hohen Lasten zufällige Fälle von zwei Instanzen der Klasse, was zu Inkonsistenzen in den Berichten führte.
Geschichte
In der Logging-Bibliothek wurde das Singleton über ein privates statisches Objekt implementiert, jedoch traten bei der Serialisierung und anschließenden Deserialisierung (z.B. in einer Cluster-Umgebung) mehrere Instanzen auf. Das Problem wurde durch die Hinzufügung von readResolve() gelöst.
Geschichte
In der Analysesystem hat der Entwickler ein thread-sicheres Singleton über die synchronisierte Methode getInstance() implementiert. In einem hochbelasteten System kam es während eines Spitzenlastzeitraums zu einem plötzlichen Leistungsabfall, und es stellte sich heraus, dass die Aufrufe von getInstance() (Tausende pro Sekunde) sich gegenseitig blockierten aufgrund unnötiger Synchronisation, obwohl die Initialisierung nur einmal nötig war.