Frage zum JMM und zur Semantik bezüglich flüchtiger Felder, die in einen synchronisierten Block geschrieben, aber nicht synchronisiert gelesen werden.
In einer ersten Version des folgenden Codes habe ich den Zugriff nicht synchronisiert, da er für frühere Anforderungen unnötig war (und eine Selbstzuweisung missbraucht hat. this.cache = this.cache stellte die volatile Schreibsemantik sicher). Bestimmte Anforderungen haben sich geändert und erfordern eine Synchronisierung, um sicherzustellen, dass doppelte Aktualisierungen nicht gesendet werden. Die Frage, die ich habe, ist, schließt der Synchronisierungsblock die Selbstzuweisung des flüchtigen Feldes aus?
%Vor%Ohne Synchronisation glaube ich, dass das flüchtige Schreiben der Selbstzuweisung technisch notwendig ist (obwohl die IDE dies als unwirksam bezeichnet). Mit dem synchronisierten Block, denke ich, ist es immer noch notwendig (da das Lesen unsynchronisiert ist), aber ich möchte nur bestätigen, da es im Code lächerlich aussieht, wenn es nicht wirklich benötigt wird. Ich bin nicht sicher, ob es irgendwelche Garantien gibt, die mir zwischen dem Ende eines synchronisierten Blocks und einem flüchtigen Lesen nicht bekannt sind.
Ja, Sie benötigen immer noch das volatile Schreiben gemäß Java Memory Model.
Es gibt keine Synchronisierungsreihenfolge beim Entsperren von cache
zu einem nachfolgenden flüchtigen Lesen von cache
:
entsperren - & gt; volatileRead garantiert keine Sichtbarkeit.
Du brauchst entweder entsperren - & gt; Sperren oder volatileWrite - & gt; volatileRead .
Allerdings haben echte JVMs viel stärkere Speichergarantien. Normalerweise haben unlock und volatileWrite denselben Speichereffekt (selbst wenn sie sich auf verschiedenen Variablen befinden); Gleich wie lock und volatileRead .
Wir haben also ein Dilemma. Die typische Empfehlung ist, dass Sie die Spezifikation strikt befolgen sollten. Es sei denn, Sie haben ein sehr breites Wissen in dieser Angelegenheit. Zum Beispiel kann ein JDK-Code einige Tricks anwenden, die theoretisch nicht korrekt sind; aber der Code zielt auf eine bestimmte JVM und der Autor ist ein Experte.
Der relative Overhead des extra flüchtigen Schreibvorgangs scheint sowieso nicht so groß zu sein.
Ihr Code ist korrekt und effizient; aber es ist außerhalb von typischen Mustern; Ich würde es ein bisschen wie zwicken:
%Vor%Ein Index, der in ein flüchtiges Array schreibt, hat tatsächlich keine Memory-Effekte. Das heißt, wenn Sie das Array bereits instanziiert haben, wird Ihnen das Feld volatile nicht die Speichersemantik geben, nach der Sie suchen, wenn Sie Elementen innerhalb des Arrays zuweisen.
Mit anderen Worten
%Vor%Hat die gleiche Speichersemantik wie
%Vor% Aus diesem Grund müssen Sie bei allen Lese- und Schreibvorgängen auf das Array synchronisieren. Und wenn ich "gleiche Speichersemantik" sage, meine ich, dass es keine Garantie gibt, dass Threads den aktuellsten Wert von cache[row][col]
Die Selbstzuweisung stellt sicher, dass ein anderer Thread den Array-Verweis liest, der festgelegt wurde, und nicht einen anderen Array-Verweis. Aber möglicherweise haben Sie einen Thread, der das Array ändert, während ein anderer Thread es liest.
Sowohl die Lesevorgänge als auch die Schreibvorgänge im Array sollten synchronisiert werden. Außerdem würde ich Arrays nicht blind speichern und an einen Cache zurückgeben. Ein Array ist eine veränderbare, nicht threadsichere Datenstruktur und jeder Thread könnte den Cache durch Mutation des Arrays beschädigen. Sie sollten erwägen, defensive Kopien zu erstellen und / oder eine nicht änderbare Liste anstelle eines Byte-Arrays zurückzugeben.
Tags und Links java multithreading thread-safety