Wie tief schwankende Veröffentlichung garantiert?

8

Wie bekannt ist, garantieren wir, dass wenn wir eine Objektreferenz haben und diese Referenz ein letztes Feld hat - wir sehen alle erreichbaren Felder vom letzten Feld (zumindest wenn der Konstruktor fertig war)

Beispiel 1:

%Vor%

Wie ich in diesem Fall gesagt habe, haben wir die Garantie, dass bar() method immer object ausgibt, weil:
1. Ich habe den vollständigen Code der Klasse Foo aufgelistet und die Karte ist final;
2. Wenn ein Thread den Verweis auf Foo und diesen Verweis! = Null sehen wird, haben wir Garantien, dass reachable vom letzten map -Referenzwert tatsächlich sein wird.

denke ich auch das

Beispiel 2:

%Vor%

Hier haben wir die gleichen Garantien über bar() Methode, aber bar2 kann NullPointerException werfen, obwohl nonFinalMap Zuweisung vor map Zuweisung auftritt.

Ich möchte wissen, wie flüchtig:

Beispiel 3:

%Vor%

Wie ich weiß bar() method kann NullPoinerException nicht ausgeben, aber es kann null ; (Ich bin mir über diesen Aspekt nicht ganz sicher)

Beispiel 4:

%Vor%

Ich denke, hier haben wir die gleichen Garantien über bar() method auch bar2() kann NullPointerException nicht werfen, weil nonVolatileMap Zuweisung eine höhere flüchtige Zuordnungszuweisung geschrieben hat, aber es kann null ausgeben

Hinzugefügt nach Elliott Frisch Kommentar

Veröffentlichung durch Rennbeispiel:

%Vor%

Bitte überprüfen oder korrigieren Sie meine Kommentare zu Code-Snippets.

    
gstackoverflow 02.02.2017, 21:31
quelle

1 Antwort

22

Im Bereich des aktuellen Java-Speichermodells ist volatile nicht gleich final . Mit anderen Worten: Sie können final nicht durch% co_de ersetzen % und denke, dass die Garantien für eine sichere Konstruktion gleich sind. Vor allem kann dies theoretisch passieren:

%Vor%

Das Schreiben des Feldes volatile im Konstruktor ist also nicht so sicher.

Intuition: Im obigen Beispiel gibt es ein Rennen auf volatile . Dieses Rennen wird nicht durch das Feld m M.x eliminiert, nur die volatile selbst m würde helfen. Mit anderen Worten: volatile modifier in diesem Beispiel ist am falschen Platz , um nützlich zu sein. In der sicheren Veröffentlichung müssen Sie "schreiben - & gt; flüchtiges Schreiben - & gt; flüchtiges Lesen, das volatiles Schreiben beobachtet - & gt; liest (jetzt beobachtet Schreiben vor dem volatilen Schreiben)", und stattdessen haben Sie "flüchtiges Schreiben - & gt; schreiben - & gt; lesen - & gt; flüchtiges Lesen (das nicht den flüchtigen Schreibvorgang beobachtet) ".

Trivia 1: Diese Eigenschaft bedeutet, dass wir volatile -s in Konstruktoren viel aggressiver optimieren können. Dies bestätigt die Intuition, dass unbeobachtetes flüchtiges Material (und tatsächlich wird es nicht beobachtet werden, bis Konstruktor mit nicht-entweichendem volatile endet) entspannt werden kann.

Trivia 2: Dies bedeutet auch, dass Sie this Variablen nicht sicher initialisieren können. Ersetzen Sie volatile durch M im obigen Beispiel, und Sie haben ein eigenartiges reales Verhalten! Rufen Sie AtomicInteger in einem Thread auf, veröffentlichen Sie die Instanz unsicher und führen Sie new AtomicInteger(42) in einem anderen Thread aus - sehen Sie get() ? JMM sagt, wie gesagt, "nein". Neuere Überarbeitungen des Java-Speichermodells versuchen, eine sichere Konstruktion für alle Initialisierungen zu gewährleisten, um diesen Fall zu erfassen. Und viele Nicht-x86-Ports, auf die es ankommt haben dies bereits verstärkt , um sicher zu sein.

Trivia 3: Doug Lea : "Dieses 42 vs final Problem hat zu einigen twisty Konstruktionen in java.util.concurrent geführt, um 0 als Basis / Default-Wert in Fällen zuzulassen, in denen es nicht natürlich sein würde. Diese Regel ist saugt und sollte geändert werden."

Das heißt, das Beispiel kann listiger gemacht werden:

%Vor%

Wenn transitiv gelesen wird volatile field nachdem volatile read den von volatile write im Konstruktor geschriebenen Wert beobachtet hat, treten die üblichen sicheren Veröffentlichungsregeln ein.

    
Aleksey Shipilev 10.02.2017, 08:51
quelle