2011-01-16 7 views
8

Ich habe drei Möglichkeiten zur Instantiierung eines Singleton gefunden, aber ich habe Zweifel, ob einer von ihnen der beste ist. Ich benutze sie in einer Multi-Thread-Umgebung und bevorzuge faule Instanziierung.
Probe 1:Java Singleton Instanziierung

private static final ClassName INSTANCE = new ClassName(); 

public static ClassName getInstance() { 
    return INSTANCE; 
} 

Probe 2:

private static class SingletonHolder { 
    public static final ClassName INSTANCE = new ClassName(); 
} 

public static ClassName getInstance() { 
    return SingletonHolder.INSTANCE; 
} 

Probe 3:

private static ClassName INSTANCE; 

public static synchronized ClassName getInstance() 
{ 
    if (INSTANCE == null) 
     INSTANCE = new ClassName(); 

    return INSTANCE; 
} 

Das Projekt I ATM bin mit verwendet Probe 2 überall, aber ich mag Probe 3 mehr. Es gibt auch die Enum-Version, aber ich verstehe es einfach nicht.

Die Frage hier ist - in welchen Fällen sollte/sollte ich keine dieser Variationen verwenden? Ich suche jedoch keine langen Erklärungen (es gibt viele andere Themen dazu, aber alle werden schließlich dazu gebracht, IMO zu streiten), ich möchte, dass es mit wenigen Worten verständlich ist.

Antwort

18

Die sicherste und einfache Weise ein Singleton in Java zu implementieren ist von Aufzählungen mit (wie Sie erwähnt):

public enum ClassName { 
    INSTANCE; 

    // fields, setters and getters 
} 

Die Enum Semantik garantiert, dass es ein INSTANCE

Wenn nicht nur sein Mit dem Enum-Ansatz müssen Sie auf eine ganze Reihe von Aspekten achten, wie Race Conditions und Reflektion. Ich habe Singles von einigen Frameworks gebrochen und sie missbraucht, weil sie nicht richtig geschrieben wurden. Das Enum garantiert, dass niemand es brechen wird.

+0

Aber wie benutze ich es? Zum Beispiel habe ich folgendes: öffentliche Klasse MyClass { öffentliche enum Klassenname { INSTANCE; // setze ich alle meine Methoden hier anstelle von ... } // hier, wie ich normalerweise würde? } Edit: verdammt, keine Formatierung Kommentare? Hasse es! – jurchiks

+1

schreiben Sie Ihre Singleton-Felder und Methoden unten. Und dann Zugriff auf sie über 'ClassName.INSTANCE.doSomething (foo)' – Bozho

+0

@ Jurchiks: ja. An anderer Stelle würden Sie 'ClassName.INSTANCE.methodName()' aufrufen, um die Singleton-Methoden zu verwenden (oder vielleicht statischen Import verwenden, um den 'ClassName.INSTANCE.') Teil zu entfernen. – Carl

1

Beispiel 1: Wird verwendet, wenn Sie kein faules Singleton benötigen.
Beispiel 2: Niemals verwenden - es wird mit zu vielen Klassen verwirrend. Und eine innere Klasse, nur um eine Variable zu halten, scheint ein bisschen unnötig zu sein. Beispiel 3: Dies ist ein fauler Singleton. Verwenden Sie es, wenn Sie es brauchen.

2

Zuerst stellen Sie unbedingt sicher, dass Sie ein Singleton benötigen und dass Sie den "Global-Level" -Zugriff auf das Singleton bereitstellen möchten. Ich habe festgestellt, dass Clients des Singletons in vielen Fällen nicht wissen müssen, dass es ein Singleton ist. Sie müssen nur einen Service erhalten, wenn sie instanziiert werden.

Unabhängig davon, wie Sie den Singleton (wenn überhaupt) erhalten, sollten Sie daher die Art und Weise ändern, wie Ihre Klassen Zugriff auf dieses Objekt erhalten. Während dies bedeutet, Konstruktoren zu ändern und "Verteilungsänderungen" zu ändern, habe ich festgestellt, dass Frameworks zur Abhängigkeitsinjektion die Kosten senken. Das DI-Rahmenwerk kann sich dann auch um die Singleton-Instanziierung kümmern (z. B. Guice tut das).

Abgesehen davon. Option 3 ist die typische und am häufigsten verwendete (und threadsichere) Version, mit der ich vertraut bin. Option 1 ist meistens für nicht-faule Initialisierung (nicht immer akzeptabel). Ich habe noch nie 2 gebraucht gesehen.

+0

Nun, die Klasse, die ich instanziieren möchte, enthält 2 Map-Objekte mit möglicherweise vielen Daten, zwei Methoden für jeweils eine Map, um Wert mit Schlüssel zu erhalten (und den Wert zu füllen, wenn es null ist) und zwei Methoden, die auf die Datenbank zugreifen der Wert eines ausgewählten Schlüssels, zweitens zum Einfügen/Aktualisieren/Löschen von Daten aus der Datenbank unter Verwendung vorhandener Daten). Getters/Setter hier nicht zu zählen. Denkst du, es sollte kein Singleton sein? – jurchiks

+0

Ich bin mir nicht sicher, ob Sie den Kern dessen, was ich vorschlage, bekommen. Angenommen, Sie haben alles, was Sie gerade beschrieben haben, als Schnittstelle und die entsprechende Klasse namens DBLookupService und DBLookupServiceImpl gekapselt. Für Klassen, die LookupService verwenden, spielt es keine Rolle, woher sie kommen - es kommt nur darauf an, dass sie eine Instanz von LookupService erhalten. Dies macht es auch viel einfacher zu testen. – Uri

+0

Ob es Singleton sein sollte - wahrscheinlich, aber Sie könnten an Multi-Core-optimierten Code denken, in dem Sie mehrere Instanzen verwenden (z. B. wenn Sie die Daten sinnvoll teilen könnten). Aber mein Punkt ist, dass Sie alles tun sollten, um Aufrufe von getInstance() aus dem Code zu vermeiden, der den Dienst verwendet. Dies wird Tests und zukünftige Änderungen viel einfacher machen. – Uri

4

In Beispiel 1 wird keine fazile Initialisierung verwendet.

Probe 2 und 3 sind beide faul. Probe 2 verwendet die Initialization on demand holder idiom (IODH), die keine Synchronisation Overhead hat. Daher ist es schneller als Beispiel 3.

In effektiven Java (Artikel 3) empfiehlt Joshua Bloch, dass ein Single-Element-Enum-Typ die beste Möglichkeit ist, ein Singleton zu implementieren.

Wenn Sie jedoch über den Enum-Typ unsicher sind, bleiben Sie mit IODH.

+0

Warum verwendet Sample 1 keine verzögerte Initialisierung? Ich denke es wird verwendet, da es nur eine Instanz gibt und eine Initialisierung gemacht wird. – Sam003