2015-09-28 3 views
9

synchronisiert vorzieht ich dieses answer am Ende gelesen haben, von denen die folgenden schriftlichen:flüchtige

alles, was Sie mit flüchtigen können mit synchronisiert, aber nicht umgekehrt erfolgen.

Es ist nicht klar. JLS 8.3.1.4 definiert flüchtige Felder wie folgt:

Ein Feld volatile deklariert werden kann, in welchem ​​Fall der Java-Speichermodell stellt sicher, dass alle Threads einen konsistenten Wert für die Variable (§17.4) zu sehen.

So sind die flüchtigen Felder über Speicher Sichtbarkeit. Auch, soweit ich von der Antwort, die ich zitiert habe, gekommen bin, werden Lesen und Schreiben in flüchtige Felder synchronisiert.

Die Synchronisierung garantiert wiederum, dass der einzige Thread Zugriff auf einen synchronisierten Block hat. Wie ich habe, hat es nichts mit Speicher Sichtbarkeit zu tun. Was habe ich verpasst?

+0

Das Eingeben und Verlassen eines 'synchronisierten' Blocks führt zu einer Speicherbarriere, die sicherstellt, dass Änderungen über Threads hinweg sichtbar sind. – Sneftel

Antwort

6

Tatsächlich Synchronisation wird auch in den Speicher visibilty verwandt als die JVM eine Barriere Speicher fügt in dem Ausgang des synchronisierten Block. Dies stellt sicher, dass die Ergebnisse von Schreibvorgängen durch den Thread in dem Synchronisationsblock garantiert sichtbar sind durch Lesevorgänge durch andere Threads einmal der erste Thread hat den synchronisierten Block verlassen.

Hinweis: folgende @ PaŭloEbermann Kommentar, wenn der andere Thread eine Lese-Speicherbarriere durch (indem sie in einem synchronisierten Block zum Beispiel bekommen), werden ihre lokalen Cache nicht ungültig gemacht werden und daher könnten sie einen alten Wert lesen.

Ausgang eines synchronisierten Block ist ein geschieht-before in diesem doc: http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility

suchen diese Extrakte:

Die Ergebnisse eines Schreib von einem Thread sind garantiert für einen nur von einem anderen Thread gelesen werden, wenn der Schreibvorgang vor dem Lesevorgang stattfindet.

und

Ein unlock (synchronisierten Block oder Methode exit) ein Monitors geschieht-vor jeder nachfolgenden Sperre (synchronisierten Block oder Methode entry) desselben Monitors.Und weil die passiert-vorher-Beziehung transitiv ist, passieren alle Aktionen eines Threads vor dem Entsperren - vor allen Aktionen, die auf einen Thread folgen, der diesen Monitor sperrt.

+0

Ausgezeichnete Antwort, vielen Dank. Ich dachte nicht, dass es unter dem Konzept der Gedächtnisvisibilität einen allgemeineren Begriff gab. –

+0

Ich würde das etwas anders verstehen: Änderungen in (und eigentlich auch vorher) dem Synchronisationsblock sind nur für andere Threads sichtbar, die danach auf dasselbe Objekt synchronisieren (oder eine andere Möglichkeit haben, in einer "happen-after" Relation zu sein) , nicht zu allen liest von allen anderen Threads. –

+0

@ PaŭloEbermann in der Tat, wenn andere Threads nicht synchronisieren (oder provozieren in irgendeiner Weise eine Lesespeicherbarriere), ihre lokalen Cache wird nicht ungültig und daher könnte sie einen alten Wert lesen. –

1

Das ist falsch. Die Synchronisation hat mit der Sichtbarkeit des Speichers zu tun. Jeder Thread hat einen eigenen Cache. Wenn Sie eine Sperre erhalten haben, ist der Cache refresehd. Wenn Sie eine Sperre freigeben, wird der Cache in den Hauptspeicher übertragen.

Wenn Sie ein flüchtiges Feld lesen, gibt es auch eine Aktualisierung, wenn Sie ein flüchtiges Feld schreiben, gibt es eine Flush.

2

Synchronisiert und flüchtig sind unterschiedlich, aber normalerweise werden beide verwendet, um das gleiche allgemeine Problem zu lösen.

Synchronisiert ist, um sicherzustellen, dass nur ein Thread auf die freigegebene Ressource zu einem bestimmten Zeitpunkt zugreifen wird.

Während diese freigegebenen Ressourcen oft als flüchtig deklariert werden, liegt das daran, dass ein Thread, der den Wert der freigegebenen Ressource geändert hat, auch im anderen Thread aktualisiert werden muss. Aber ohne flüchtig, die Laufzeit, optimiert nur den Code, indem sie den Wert aus dem Cache liest. Was also flüchtig ist, ist, wenn ein Thread-Zugriff flüchtig ist, wird er den Wert nicht aus dem Cache lesen, sondern er erhält ihn tatsächlich aus dem tatsächlichen Speicher und derselbe wird verwendet.


War durch log4j Code und das ist, was ich gefunden habe.

/** 
* Config should be consistent across threads. 
*/ 
protected volatile PrivateConfig config; 
+0

Dann warum "flüchtige" Schlüsselwort überhaupt verwenden? Sie können multi-thread-Zugriff erhalten, ohne volatile oder synchronisiert. –

1

Wenn mehrere Threads auf einem gemeinsam genutzten flüchtigen Variable schreiben und sie müssen auch einen vorherigen Wert, es zu benutzen, kann es eine race condition erstellen. An diesem Punkt müssen Sie die Synchronisierung verwenden.

... Wenn zwei Threads eine freigegebene Variable lesen und gleichzeitig schreiben, ist die Verwendung des Schlüsselworts volatile nicht ausreichend. Sie müssen in diesem Fall eine Synchronisierung verwenden, um sicherzustellen, dass das Lesen und Schreiben der Variablen atomar ist. Das Lesen oder Schreiben einer flüchtigen Variablen blockiert das Lesen oder Schreiben von Threads nicht. Dazu müssen Sie das synchronisierte Schlüsselwort um kritische Abschnitte herum verwenden.

Ausführliche Tutorial über flüchtige finden 'volatile' is not always enough.

+1

Für einfache Fälle wie ein thread-sicherer Zähler oder cas, mit einem [Atomic] (http://docs.oracle. com/javase/7/docs/api/java/util/gleichzeitige/atomic/package-summary.html) Typ ist in der Regel einfacher und funktioniert besser als ein "synchronisierter" Block, besonders bei hoher Konkurrenz. – duckstep

Verwandte Themen