Was ist ein effizienter Weg, um ein Singleton-Muster in Java zu implementieren?
java
singleton
design-patterns
Riyaz Mohammed Ibrahim
quelle
quelle
Antworten:
Verwenden Sie eine Aufzählung:
Joshua Bloch erläuterte diesen Ansatz in seinem Vortrag " Effective Java Reloaded" bei Google I / O 2008: Link zum Video . Siehe auch Folien 30-32 seiner Präsentation ( effektiv_java_reloaded.pdf ):
Bearbeiten: Ein Online-Teil von "Effective Java" sagt:
quelle
Je nach Verwendung gibt es mehrere "richtige" Antworten.
Da Java5 der beste Weg ist, dies zu tun, ist die Verwendung einer Aufzählung:
Vor Java5 ist der einfachste Fall:
Lassen Sie uns den Code durchgehen. Zunächst möchten Sie, dass die Klasse endgültig ist. In diesem Fall habe ich das
final
Schlüsselwort verwendet, um die Benutzer darüber zu informieren, dass es endgültig ist. Dann müssen Sie den Konstruktor privat machen, um zu verhindern, dass Benutzer ihr eigenes Foo erstellen. Das Auslösen einer Ausnahme vom Konstruktor verhindert, dass Benutzer Reflektion verwenden, um ein zweites Foo zu erstellen. Anschließend erstellen Sie einprivate static final Foo
Feld für die einzige Instanz und einepublic static Foo getInstance()
Methode für die Rückgabe. Die Java-Spezifikation stellt sicher, dass der Konstruktor nur aufgerufen wird, wenn die Klasse zum ersten Mal verwendet wird.Wenn Sie ein sehr großes Objekt oder einen schweren Konstruktionscode haben UND auch über andere zugängliche statische Methoden oder Felder verfügen, die möglicherweise verwendet werden, bevor eine Instanz benötigt wird, müssen Sie nur dann eine verzögerte Initialisierung verwenden.
Sie können a verwenden
private static class
, um die Instanz zu laden. Der Code würde dann so aussehen:Da die Zeile
private static final Foo INSTANCE = new Foo();
nur ausgeführt wird, wenn die Klasse FooLoader tatsächlich verwendet wird, wird die verzögerte Instanziierung erledigt und es wird garantiert, dass sie threadsicher ist.Wenn Sie Ihr Objekt auch serialisieren möchten, müssen Sie sicherstellen, dass durch die Deserialisierung keine Kopie erstellt wird.
Die Methode stellt
readResolve()
sicher, dass die einzige Instanz zurückgegeben wird, auch wenn das Objekt in einem früheren Programmlauf serialisiert wurde.quelle
Haftungsausschluss: Ich habe gerade alle fantastischen Antworten zusammengefasst und in meinen Worten geschrieben.
Während der Implementierung von Singleton haben wir zwei Möglichkeiten:
1. Faules Laden
2. Frühes Laden
Das verzögerte Laden erhöht den Overhead (viele, um ehrlich zu sein). Verwenden Sie ihn daher nur, wenn Sie ein sehr großes Objekt oder einen schweren Konstruktionscode haben UND auch über andere zugängliche statische Methoden oder Felder verfügen, die möglicherweise verwendet werden, bevor eine Instanz benötigt wird Sie müssen die verzögerte Initialisierung verwenden. Andernfalls ist die Auswahl des frühen Ladens eine gute Wahl.
Die einfachste Art, Singleton zu implementieren, ist
Alles ist gut, bis auf den früh geladenen Singleton. Versuchen wir es mit einem faul geladenen Singleton
So weit so gut, aber unser Held wird nicht überleben, wenn er alleine mit mehreren bösen Fäden kämpft, die viele, viele Instanzen unseres Helden wollen. Schützen wir es also vor bösem Multithreading
aber es ist nicht genug, um den Helden zu beschützen, wirklich !!! Dies ist das Beste, was wir tun können / sollten, um unserem Helden zu helfen
Dies wird als "Double-Checked Locking Idiom" bezeichnet. Es ist leicht, die volatile Aussage zu vergessen und schwer zu verstehen, warum es notwendig ist.
Für Details: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
Jetzt sind wir uns über böse Fäden sicher, aber was ist mit der grausamen Serialisierung? Wir müssen sicherstellen, dass auch während der Serialisierung kein neues Objekt erstellt wird
Die Methode stellt
readResolve()
sicher, dass die einzige Instanz zurückgegeben wird, auch wenn das Objekt in einem früheren Programmlauf serialisiert wurde.Schließlich haben wir genügend Schutz gegen Threads und Serialisierung hinzugefügt, aber unser Code sieht sperrig und hässlich aus. Geben wir unserem Helden ein neues Gesicht
Ja, das ist unser gleicher Held :)
Da die Zeile
private static final Foo INSTANCE = new Foo();
nur ausgeführt wird, wenn die KlasseFooLoader
tatsächlich verwendet wird, wird die faule Instanziierung erledigt.und ist es garantiert threadsicher.
Und wir sind so weit gekommen, hier ist der beste Weg, um alles zu erreichen, was wir getan haben, der bestmögliche Weg
Welche intern behandelt werden
Das ist es! Keine Angst mehr vor Serialisierung, Threads und hässlichem Code. Auch ENUMS-Singleton werden träge initialisiert .
-Joshua Bloch in "Effektives Java"
Jetzt haben Sie vielleicht erkannt, warum ENUMS als der beste Weg zur Implementierung von Singleton angesehen werden, und vielen Dank für Ihre Geduld :)
Aktualisiert in meinem Blog .
quelle
serialVersionUID
von0L
. Drittes Problem: Keine Anpassung: Alle klassenspezifischen Methoden writeObject, readObject, readObjectNoData, writeReplace und readResolve, die durch Aufzählungstypen definiert sind, werden während der Serialisierung und Deserialisierung ignoriert.Die von Stu Thompson veröffentlichte Lösung ist in Java5.0 und höher gültig. Aber ich würde es vorziehen, es nicht zu verwenden, weil ich denke, dass es fehleranfällig ist.
Es ist leicht, die volatile Aussage zu vergessen und schwer zu verstehen, warum es notwendig ist. Ohne das flüchtige Material wäre dieser Code aufgrund des doppelt überprüften Antipatterns nicht mehr threadsicher. Weitere Informationen hierzu finden Sie in Abschnitt 16.2.4 von Java Concurrency in Practice . Kurz gesagt: Dieses Muster (vor Java5.0 oder ohne die flüchtige Anweisung) könnte einen Verweis auf das Bar-Objekt zurückgeben, das sich (noch) in einem falschen Zustand befindet.
Dieses Muster wurde zur Leistungsoptimierung erfunden. Aber das ist wirklich kein wirkliches Problem mehr. Der folgende verzögerte Initialisierungscode ist schnell und - was noch wichtiger ist - einfacher zu lesen.
quelle
getBar()
. (Und wenngetBar
es "zu früh" genannt wird, werden Sie das gleiche Problem haben, egal wie Singleons implementiert sind.) Sie können das Laden des Codes in der faulen Klasse hier oben sehen: pastebin.com/iq2eayiRThread sicher in Java 5+:
EDIT :
volatile
Achten Sie hier auf den Modifikator. :) Es ist wichtig, da andere Threads ohne sie vom JMM (Java Memory Model) nicht garantiert werden, um Änderungen an ihrem Wert zu sehen. Die Synchronisation kümmert sich nicht darum - sie serialisiert nur den Zugriff auf diesen Codeblock.EDIT 2 : Die Antwort von @Bno beschreibt den von Bill Pugh (FindBugs) empfohlenen Ansatz und ist wohl besser. Lesen Sie und stimmen Sie auch über seine Antwort ab.
quelle
Vergessen Sie die verzögerte Initialisierung , es ist zu problematisch. Dies ist die einfachste Lösung:
quelle
Stellen Sie sicher, dass Sie es wirklich brauchen. Führen Sie eine Google-Suche nach "Singleton Anti-Pattern" durch, um einige Argumente dagegen zu sehen. Ich nehme an, daran ist an sich nichts auszusetzen, aber es ist nur ein Mechanismus zum Offenlegen einiger globaler Ressourcen / Daten. Stellen Sie also sicher, dass dies der beste Weg ist. Insbesondere habe ich die Abhängigkeitsinjektion als nützlicher empfunden, insbesondere wenn Sie auch Komponententests verwenden, da DI es Ihnen ermöglicht, verspottete Ressourcen zu Testzwecken zu verwenden.
quelle
Ich bin verwirrt über einige der Antworten, die DI als Alternative zur Verwendung von Singletons vorschlagen. Dies sind nicht verwandte Konzepte. Sie können DI verwenden, um entweder Singleton- oder Nicht-Singleton-Instanzen (z. B. pro Thread) zu injizieren. Zumindest ist dies der Fall, wenn Sie Spring 2.x verwenden. Ich kann nicht für andere DI-Frameworks sprechen.
Meine Antwort auf das OP wäre also (mit Ausnahme des trivialsten Beispielcodes):
Dieser Ansatz bietet Ihnen eine schöne entkoppelte (und daher flexible und testbare) Architektur, bei der die Verwendung eines Singletons ein leicht umkehrbares Implementierungsdetail ist (vorausgesetzt, alle von Ihnen verwendeten Singletons sind natürlich threadsicher).
quelle
TicketNumberer
, die eine einzelne globale Instanz haben muss und in der Sie eine Klasse schreiben möchten, die eineTicketIssuer
Codezeile enthältint ticketNumber = ticketNumberer.nextTicketNumber();
. Im traditionellen Singleton-Denken müsste die vorherige Codezeile so etwas wie seinTicketNumberer ticketNumberer = TicketNumberer.INSTANCE;
. Im DI-Denken hätte die Klasse einen Konstruktor wiepublic TicketIssuer(TicketNumberer ticketNumberer) { this.ticketNumberer = ticketNumberer; }
.main
Methode der App (oder einer ihrer Minions) die Abhängigkeit erstellen und dann den Konstruktor aufrufen würde. Im Wesentlichen ist die Verwendung einer globalen Variablen (oder einer globalen Methode) nur eine einfache Form des gefürchteten Dienstlokalisierungsmusters und kann wie jede andere Verwendung dieses Musters durch Abhängigkeitsinjektion ersetzt werden.Überlegen Sie wirklich, warum Sie einen Singleton benötigen, bevor Sie ihn schreiben. Es gibt eine quasi-religiöse Debatte über deren Verwendung, über die Sie leicht stolpern können, wenn Sie Singletons in Java googeln.
Persönlich versuche ich aus vielen Gründen, Singletons so oft wie möglich zu vermeiden. Die meisten davon finden sich auch beim Googeln von Singletons. Ich habe das Gefühl, dass Singletons ziemlich oft missbraucht werden, weil sie für jeden leicht verständlich sind, als Mechanismus verwendet werden, um "globale" Daten in ein OO-Design zu integrieren, und weil sie das Management des Objektlebenszyklus (oder) einfach umgehen können wirklich darüber nachdenken, wie man A von innen heraus machen kann B). Schauen Sie sich Dinge wie Inversion of Control (IoC) oder Dependency Injection (DI) an, um einen schönen Mittelweg zu finden.
Wenn Sie wirklich einen brauchen, hat Wikipedia ein gutes Beispiel für eine ordnungsgemäße Implementierung eines Singletons.
quelle
Es folgen 3 verschiedene Ansätze
1) Aufzählung
2) Double Checked Locking / Lazy Loading
3) Statische Fabrikmethode
quelle
Ich verwende das Spring Framework, um meine Singletons zu verwalten. Es erzwingt nicht die "Singleton-Ness" der Klasse (was Sie sowieso nicht wirklich tun können, wenn mehrere Klassenlader beteiligt sind), bietet jedoch eine wirklich einfache Möglichkeit, verschiedene Fabriken zum Erstellen verschiedener Objekttypen zu erstellen und zu konfigurieren.
quelle
Wikipedia hat einige Beispiele für Singletons, auch in Java. Die Java 5-Implementierung sieht ziemlich vollständig aus und ist threadsicher (doppelt überprüfte Sperre angewendet).
quelle
Version 1:
Faules Laden, Thread-sicher mit Blockierung, geringe Leistung wegen
synchronized
.Version 2:
Lazy Loading, Thread-sicher mit nicht blockierend, hohe Leistung.
quelle
Wenn Sie kein verzögertes Laden benötigen, versuchen Sie es einfach
Wenn Sie ein verzögertes Laden wünschen und Ihr Singleton threadsicher sein soll, versuchen Sie es mit dem Muster der doppelten Überprüfung
Da die doppelte Überprüfung nicht garantiert funktioniert (aufgrund eines Problems mit Compilern weiß ich nichts mehr darüber), können Sie auch versuchen, die gesamte getInstance-Methode zu synchronisieren oder eine Registrierung für alle Ihre Singletons zu erstellen.
quelle
volatile
Ich würde Enum Singleton sagen
Singleton, das Enum in Java verwendet, ist im Allgemeinen eine Möglichkeit, Enum Singleton zu deklarieren. Enum Singleton kann eine Instanzvariable und eine Instanzmethode enthalten. Beachten Sie der Einfachheit halber auch, dass Sie bei Verwendung einer Instanzmethode die Thread-Sicherheit dieser Methode gewährleisten müssen, wenn sie den Status des Objekts überhaupt beeinflusst.
Die Verwendung einer Aufzählung ist sehr einfach zu implementieren und weist keine Nachteile in Bezug auf serialisierbare Objekte auf, die auf andere Weise umgangen werden müssen.
Sie können
Singleton.INSTANCE
viel einfacher darauf zugreifen , als diegetInstance()
Methode in Singleton aufzurufen .Ein weiteres Problem bei herkömmlichen Singletons besteht darin, dass sie nach der Implementierung der
Serializable
Schnittstelle nicht mehr Singleton bleiben, da diereadObject()
Methode immer eine neue Instanz wie den Konstruktor in Java zurückgibt. Dies kann vermieden werden, indemreadResolve()
neu erstellte Instanzen verwendet und verworfen werden, indem sie wie unten beschrieben durch Singleton ersetzt werdenDies kann noch komplexer werden, wenn Ihre Singleton-Klasse den Status beibehält, da Sie sie vorübergehend machen müssen. In Enum Singleton wird die Serialisierung jedoch von JVM garantiert.
Gut gelesen
quelle
quelle
Es könnte etwas spät sein, dies zu tun, aber die Implementierung eines Singletons ist sehr nuanciert. Das Haltermuster kann in vielen Situationen nicht verwendet werden. Und IMO, wenn Sie eine flüchtige Variable verwenden - Sie sollten auch eine lokale Variable verwenden. Beginnen wir am Anfang und wiederholen wir das Problem. Du wirst sehen, was ich meine.
Der erste Versuch könnte ungefähr so aussehen:
Hier haben wir die MySingleton-Klasse, die ein privates statisches Mitglied namens INSTANCE und eine öffentliche statische Methode namens getInstance () hat. Beim ersten Aufruf von getInstance () ist das INSTANCE-Mitglied null. Der Flow fällt dann in die Erstellungsbedingung und erstellt eine neue Instanz der MySingleton-Klasse. Nachfolgende Aufrufe von getInstance () stellen fest, dass die Variable INSTANCE bereits festgelegt ist, und erstellen daher keine weitere MySingleton-Instanz. Dadurch wird sichergestellt, dass es nur eine Instanz von MySingleton gibt, die von allen Aufrufern von getInstance () gemeinsam genutzt wird.
Diese Implementierung hat jedoch ein Problem. Multithread-Anwendungen haben eine Race-Bedingung für die Erstellung der einzelnen Instanz. Wenn mehrere Ausführungsthreads gleichzeitig die Methode getInstance () treffen (oder ungefähr), wird das INSTANCE-Mitglied jeweils als null angezeigt. Dies führt dazu, dass jeder Thread eine neue MySingleton-Instanz erstellt und anschließend das INSTANCE-Mitglied festlegt.
Hier haben wir das Schlüsselwort synchronized in der Methodensignatur verwendet, um die Methode getInstance () zu synchronisieren. Dies wird sicherlich unseren Rennzustand verbessern. Threads blockieren nun die Methode und geben sie einzeln ein. Es entsteht aber auch ein Leistungsproblem. Diese Implementierung synchronisiert nicht nur die Erstellung der einzelnen Instanz, sondern synchronisiert alle Aufrufe von getInstance (), einschließlich Lesevorgängen. Lesevorgänge müssen nicht synchronisiert werden, da sie einfach den Wert von INSTANCE zurückgeben. Da Lesevorgänge den Großteil unserer Aufrufe ausmachen (denken Sie daran, dass die Instanziierung nur beim ersten Aufruf erfolgt), wird durch die Synchronisierung der gesamten Methode ein unnötiger Leistungseinbruch verursacht.
Hier haben wir die Synchronisation von der Methodensignatur in einen synchronisierten Block verschoben, der die Erstellung der MySingleton-Instanz umschließt. Aber löst dies unser Problem? Nun, wir blockieren keine Lesevorgänge mehr, aber wir haben auch einen Schritt zurück gemacht. Mehrere Threads treffen gleichzeitig oder ungefähr zur gleichen Zeit auf die Methode getInstance () und alle sehen das INSTANCE-Mitglied als null. Sie treffen dann den synchronisierten Block, wo man die Sperre erhält und die Instanz erstellt. Wenn dieser Thread den Block verlässt, kämpfen die anderen Threads um die Sperre, und jeder Thread fällt nacheinander durch den Block und erstellt eine neue Instanz unserer Klasse. Wir sind also wieder da, wo wir angefangen haben.
Hier stellen wir einen weiteren Check von INSIDE the block aus. Wenn das INSTANCE-Mitglied bereits festgelegt wurde, überspringen wir die Initialisierung. Dies wird als doppelt geprüftes Sperren bezeichnet.
Dies löst unser Problem der Mehrfachinstanziierung. Aber auch hier hat unsere Lösung eine weitere Herausforderung dargestellt. Andere Threads sehen möglicherweise nicht, dass das INSTANCE-Mitglied aktualisiert wurde. Dies liegt daran, wie Java die Speicheroperationen optimiert. Threads kopieren die ursprünglichen Werte von Variablen aus dem Hauptspeicher in den Cache der CPU. Änderungen an Werten werden dann in diesen Cache geschrieben und aus diesem gelesen. Dies ist eine Funktion von Java zur Optimierung der Leistung. Dies schafft jedoch ein Problem für unsere Singleton-Implementierung. Ein zweiter Thread, der von einer anderen CPU oder einem anderen Kern unter Verwendung eines anderen Caches verarbeitet wird, sieht die vom ersten vorgenommenen Änderungen nicht. Dies führt dazu, dass der zweite Thread das INSTANCE-Mitglied als null ansieht und das Erstellen einer neuen Instanz unseres Singletons erzwingt.
Wir lösen dies, indem wir das flüchtige Schlüsselwort in der Deklaration des INSTANCE-Mitglieds verwenden. Dadurch wird der Compiler angewiesen, immer aus dem Hauptspeicher und nicht aus dem CPU-Cache zu lesen und in diesen zu schreiben.
Diese einfache Änderung ist jedoch mit Kosten verbunden. Da wir den CPU-Cache umgehen, werden wir jedes Mal, wenn wir mit dem flüchtigen INSTANCE-Mitglied arbeiten, einen Leistungseinbruch erleiden - was wir viermal tun. Wir überprüfen die Existenz (1 und 2), setzen den Wert (3) und geben dann den Wert (4) zurück. Man könnte argumentieren, dass dieser Pfad der Randfall ist, da wir die Instanz nur beim ersten Aufruf der Methode erstellen. Vielleicht ist ein Leistungseinbruch bei der Erstellung erträglich. Aber selbst unser Hauptanwendungsfall, liest, wird das flüchtige Mitglied zweimal bearbeiten. Einmal, um die Existenz zu überprüfen und erneut, um ihren Wert zurückzugeben.
Da der Leistungseinbruch darauf zurückzuführen ist, dass das flüchtige Element direkt bearbeitet wird, setzen wir eine lokale Variable auf den Wert des flüchtigen Elements und arbeiten stattdessen mit der lokalen Variablen. Dies wird die Häufigkeit verringern, mit der wir auf dem volatilen Markt operieren, wodurch ein Teil unserer Leistungsverluste zurückgewonnen wird. Beachten Sie, dass wir unsere lokale Variable erneut setzen müssen, wenn wir den synchronisierten Block eingeben. Dies stellt sicher, dass alle Änderungen, die während des Wartens auf das Schloss vorgenommen wurden, auf dem neuesten Stand sind.
Ich habe kürzlich einen Artikel darüber geschrieben. Den Singleton dekonstruieren . Dort finden Sie weitere Informationen zu diesen Beispielen und ein Beispiel für das "Halter" -Muster. Es gibt auch ein reales Beispiel, das den doppelt überprüften volatilen Ansatz zeigt. Hoffe das hilft.
quelle
BearerToken instance
in Ihrem Artikel nicht der Fall iststatic
? Und was ist dasresult.hasExpired()
?class MySingleton
- vielleicht sollte es seinfinal
?BearerToken
Instanz ist nicht statisch, da sie Teil derBearerTokenFactory
- ist, die mit einem bestimmten Autorisierungsserver konfiguriert ist. Es kann vieleBearerTokenFactory
Objekte geben - jedes hat sein eigenes "Cache"BearerToken
, das es verteilt, bis es abgelaufen ist. DiehasExpired()
Methode auf demBeraerToken
wird in der Factory-get()
Methode aufgerufen , um sicherzustellen, dass kein abgelaufenes Token ausgegeben wird. Wenn abgelaufen, wird ein neues Token vom Autorisierungsserver angefordert. Der Absatz nach dem Codeblock erläutert dies ausführlicher.So implementieren Sie ein einfaches
singleton
:So erstellen Sie richtig faul
singleton
:quelle
getInstance()
. Aber in der Tat, wenn Sie keine anderen statischen Methoden in Ihrer Klasse habenSingleton
und nur aufrufen,getInstance()
gibt es keinen wirklichen Unterschied.Sie müssen die Redewendung überprüfen, wenn Sie die Instanzvariable einer Klasse träge laden müssen. Wenn Sie eine statische Variable oder einen Singleton träge laden müssen, müssen Sie die Initiierung bei Bedarf in Anspruch nehmen .
Wenn der Singleton seriliazble sein muss, müssen außerdem alle anderen Felder vorübergehend sein und die readResolve () -Methode muss implementiert werden, um das Singleton-Objekt invariant zu halten. Andernfalls wird jedes Mal, wenn das Objekt deserialisiert wird, eine neue Instanz des Objekts erstellt. ReadResolve () ersetzt das neue Objekt, das von readObject () gelesen wurde, wodurch das neue Objekt zur Speicherbereinigung gezwungen wurde, da keine Variable darauf verweist.
quelle
Verschiedene Möglichkeiten, ein Singleton-Objekt zu erstellen:
Laut Joshua Bloch wäre Enum das Beste.
Sie können auch Double Check Locking verwenden.
Sogar die innere statische Klasse kann verwendet werden.
quelle
Enum Singleton
Der einfachste Weg, einen Singleton zu implementieren, der threadsicher ist, ist die Verwendung einer Aufzählung
Dieser Code funktioniert seit der Einführung von Enum in Java 1.5
Doppelte Verriegelung
Wenn Sie einen „klassischen“ Singleton codieren möchten, der in einer Multithread-Umgebung (ab Java 1.5) funktioniert, sollten Sie diesen verwenden.
Dies ist vor 1.5 nicht threadsicher, da die Implementierung des flüchtigen Schlüsselworts anders war.
Frühes Laden von Singleton (funktioniert bereits vor Java 1.5)
Diese Implementierung instanziiert den Singleton beim Laden der Klasse und bietet Thread-Sicherheit.
quelle
Verwenden Sie für JSE 5.0 und höher den Enum-Ansatz, andernfalls verwenden Sie den statischen Singleton-Holder-Ansatz ((ein von Bill Pugh beschriebener Lazy-Loading-Ansatz). Die letzte Lösung ist auch threadsicher, ohne dass spezielle Sprachkonstrukte erforderlich sind (dh flüchtig oder synchronisiert).
quelle
Ein weiteres Argument, das häufig gegen Singletons verwendet wird, sind ihre Testbarkeitsprobleme. Singletons sind zu Testzwecken nicht leicht zu verspotten. Wenn sich herausstellt, dass dies ein Problem ist, möchte ich die folgenden geringfügigen Änderungen vornehmen:
Die hinzugefügte
setInstance
Methode ermöglicht das Festlegen einer Modellimplementierung der Singleton-Klasse während des Testens:Dies funktioniert auch mit frühen Initialisierungsansätzen:
Dies hat den Nachteil, dass diese Funktionalität auch der normalen Anwendung zugänglich gemacht wird. Andere Entwickler, die an diesem Code arbeiten, könnten versucht sein, die Methode "setInstance" zu verwenden, um eine bestimmte Funktion und damit das gesamte Anwendungsverhalten zu ändern. Daher sollte diese Methode mindestens eine gute Warnung in ihrem Javadoc enthalten.
Für die Möglichkeit von Mockup-Tests (falls erforderlich) kann diese Code-Exposition jedoch ein akzeptabler Preis sein.
quelle
einfachste Singleton-Klasse
quelle
Ich denke immer noch, dass enum nach Java 1.5 die beste verfügbare Singleton-Implementierung ist, da es auch sicherstellt, dass selbst in Umgebungen mit mehreren Threads nur eine Instanz erstellt wird.
public enum Singleton{ INSTANCE; }
und du bist fertig !!!
quelle
Schauen Sie sich diesen Beitrag an.
Beispiele für GoF-Entwurfsmuster in Javas Kernbibliotheken
Aus dem Abschnitt "Singleton" der besten Antwort:
Sie können das Beispiel von Singleton auch aus nativen Java-Klassen selbst lernen.
quelle
Das beste Singleton-Muster, das ich je gesehen habe, verwendet die Lieferantenschnittstelle.
Siehe unten:
quelle
Manchmal reicht ein einfaches "
static Foo foo = new Foo();
" nicht aus. Denken Sie nur an einige grundlegende Daten, die Sie einfügen möchten.Andererseits müssten Sie jede Methode synchronisieren, die die Singleton-Variable als solche instanziiert. Die Synchronisierung ist als solche nicht schlecht, kann jedoch zu Leistungsproblemen oder Sperren führen (in sehr sehr seltenen Situationen anhand dieses Beispiels. Die Lösung ist
Was passiert nun? Die Klasse wird über den Klassenlader geladen. Direkt nachdem die Klasse aus einem Byte-Array interpretiert wurde, führt die VM den statischen {} - Block aus. Das ist das ganze Geheimnis: Der statische Block wird nur einmal aufgerufen, wenn die angegebene Klasse (Name) des angegebenen Pakets von diesem einen Klassenladeprogramm geladen wird.
quelle
Da wir das Schlüsselwort Synchronized vor getInstance hinzugefügt haben, haben wir die Race-Bedingung für den Fall vermieden, dass zwei Threads gleichzeitig getInstance aufrufen.
quelle