Warum ist flüchtig verwendet in double-checked locking
Vom Kopf Zuerst design-patterns-Buch, das singleton-Muster mit double-checked locking umgesetzt worden ist, wie folgt:
public class Singleton {
private volatile static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
Ich verstehe nicht, warum volatile
verwendet wird. Nicht volatile
Nutzung Niederlage der Zweck der Verwendung von double-checked locking ich.e Leistung?
- Ich dachte, doppelt geprüft Verriegelung gebrochen war, hat man es beheben?
- Für was es Wert ist, fand ich Head First design patterns zu werden, ein schreckliches Buch, um daraus zu lernen. Als ich mich wieder auf ihn, macht es durchaus Sinn, jetzt, dass ich gelernt habe, das Muster anderswo, sondern zu lernen, ohne zu wissen, die Muster, die es wirklich nicht dienen, es Zweck. Aber es ist sehr beliebt, so war es vielleicht nur mich dichten. 🙂
- Ich habe gesehen, dieses Beispiel als eine Art, in der die jvm vertraut werden kann zu tun, die DCL.
- FWIW, auf einem x86-system einen flüchtigen Lesen-Lesen ist, soll in einer nicht-op. In der Tat, die einzige operation, die erfordert, dass ein Zaun für die Speicher-Konsistenz ist ein flüchtiger Schreib-Lese. Also wenn du wirklich nur schreiben, der Wert einmal, dann sollte es nur minimale Auswirkungen. Ich habe nicht gesehen, dass jemand tatsächlich die benchmark und, denken, das Ergebnis wäre interessant!
- für alle praktischen Gründen, es ist immer noch gebrochen. Es ist besser, mit
volatile
(wie in "nicht Schrauben Ihrem Programm"), aber dann braucht man auch nicht wirklich viel gewinnen. - Na ja, offensichtlich haben wir nur einen Effekt für das schreiben-Lesen (wenn alles, was wir tun, ist zu Lesen, es ist eher uninteressant, die identische Kopie, die wir gerade Lesen), aber "Nur selten führt zu einem Fehler" ist nicht sehr hilfreich, denke ich - eigentlich macht es viel schwieriger, das problem zu identifizieren.
- Ich habe nie gesagt, "Nur selten die Ergebnisse in ein Fehler", mein Punkt war, dass in der Theorie sollte es nur einen Treffer, um die Leistung kurz nach der Initialisierung.
- Sie falsch verstanden - ja, ich Stimme zu.
- Related posts hier und hier über, warum die doppelte überprüfung ist auch erforderlich, in den ersten Platz.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Eine gute Ressource für das Verständnis, warum
volatile
benötigt wird, kommt aus dem JCIP Buch. Wikipedia hat eine anständige Erklärung des Materials als auch.Das eigentliche problem ist, dass
Thread A
berechtigt, einen Speicherplatz für dieinstance
bevor es fertig ist, Bauinstance
.Thread B
werden sehen, dass die Zuordnung und versuchen, es zu benutzen. Dies führt zuThread B
scheitern, weil es ist mit einer teilweise konstruiert version voninstance
.volatile
ist hier bloß zu zeigen, dass es gibt einen Weg,, um das gebrochene Muster arbeiten. Es ist mehr ein coding-Herausforderung, als ein echtes problem.main
.Foo.getInstance()
ist nur ein Ausdruck, mit dem eine Foo irgendwie, es unterscheidet sich nicht von@Inject Foo foo
; so oder so wird die Website, die Anforderungen, die ein Foo ist Agnostiker, von denen Foo zurückgegeben wird, und wie, so oder so statische und runtime-Abhängigkeiten sind die gleichen.static
factory
ist die Versuchung zu nennen, die tief im inneren code, die, sollten Sie nicht wissen, dergetInstance()
Methode und stattdessen Nachfrage eine Instanz zur Verfügung gestellt werden.The real problem is that Thread A may assign a memory space for instance before it is finished constructing instance.
So wievolatile
dieses Problem beheben ?volatile
?Zitiert von @irreputable, volatile ist nicht teuer. Auch wenn es teuer ist, die Konsistenz sollte Vorrang vor Leistung.
Gibt es noch eine saubere eleganten Weg für Faule Singletons.
Quelle Artikel : Initialisierung-auf-demand_holder_idiom aus wikipedia
Da die Klasse keine
static
Variablen zu initialisieren, die Initialisierung abgeschlossen ist trivial.Die statische definition der Klasse
LazyHolder
es ist nicht initialisiert, bis die JVM bestimmt, dass LazyHolder ausgeführt werden müssen.Die statische Klasse
LazyHolder
wird nur ausgeführt, wenn die statische MethodegetInstance
aufgerufen wird die Klasse Singleton, und das erste mal das passiert ist, die JVM zu laden und initialisieren Sie dieLazyHolder
Klasse.Diese Lösung ist thread-sicher, ohne dass spezielle Sprachkonstrukte (z.B.
volatile
odersynchronized
).Gut, es gibt keine double-checked-locking für die Leistung. Es ist ein gebrochen Muster.
Verlassen Emotionen beiseite,
volatile
hier ist, weil ohne Sie durch die Zeit, die zweiten thread gehtinstance == null
, ersten thread vielleicht nicht konstruierennew Singleton()
noch: niemand verspricht, dass die Erstellung des Objekts passiert-vor Zuordnung zuinstance
für einen thread, aber den man eigentlich erstellen des Objekts.volatile
wiederum stellt passiert-vor Zusammenhang zwischen lese-und Schreibvorgängen, und behebt das gebrochene Muster.Wenn Sie sich für Leistung, Einsatz-Halter-innere statische Klasse statt.
Wenn Sie nicht haben, ein zweiter thread könnte sich in den synchronized-block nach dem ersten Satz auf null, und Ihren lokalen cache immer noch denke, es war null.
Die erste ist nicht für die Richtigkeit (wenn es Sie sind richtig, dass es wäre selbstzerstörerisch), sondern vielmehr für die Optimierung.
Einen flüchtigen Lesen ist nicht wirklich teuer, in sich selbst.
Entwerfen Sie einen test, um zu rufen
getInstance()
in einer engen Schleife, zu beobachten, die Auswirkungen von flüchtigen Lesen; aber dass dieser test nicht realistisch ist; in einer solchen situation, Programmierer in der Regel nennen würdegetInstance()
einmal-und cache-Instanz für die Dauer des Einsatzes.Anderen impl ist die Verwendung eines
final
Feld (siehe wikipedia). Dies erfordert eine zusätzliche Lesen, das kann teurer werden als dievolatile
version. Diefinal
version kann schneller in eine enge Schleife, aber der test ist umstritten, wie zuvor argumentiert.Deklarieren Sie die variable als
volatile
gewährleistet, dass alle Zugriffe auf die es tatsächlich Lesen Sie den aktuellen Wert aus dem Speicher.Ohne
volatile
kann der compiler optimieren, entfernt den Speicher zugreift und halten Ihren Wert in ein register, so wird nur die erste Verwendung der variable liest den eigentlichen Speicherort, halten Sie die variable. Dies ist ein problem, wenn die variable geändert wird, die von einem anderen thread zwischen dem ersten und dem zweiten Zugang; der erste thread ist nur eine Kopie der ersten (vorher geänderte) Wert, also der zweiteif
Anweisung testet eine veraltete Kopie der variable Wert.volatile
. Registriert, haben nichts zu tun mit unvollständigen Objekten und DCL problem.volatile
ist zu schmal - wenn das alles war flüchtig Tat, doppelt geprüft sperren würde, habe funktioniert nur im <Java5.volatile
führt eine memory barrier, bestimmte Neuordnung illegal - ohne, dass, selbst wenn wir Sie nie Lesen veralteten Werte aus dem Speicher, es wäre immer noch unsicher. Edit: alf, mich zu schlagen, sollte nicht bekommen haben mich einige nette Tee 😉volatlie
Referenz auf ein singleton macht dein thread wieder Lesen Hauptspeicher—aber es ist ein Nebeneffekt, nicht die Ursache des Problems:))