Warum Volatile sich seltsam benimmt

8

Ich habe dieses merkwürdige Verhalten des volatilen Keywords kürzlich erfahren. Soweit ich weiß,

    Das flüchtige Schlüsselwort
  1. wird auf die Variable angewendet, um die an den Daten vorgenommenen Änderungen widerzuspiegeln die Variable um einen Thread auf den anderen Thread.

  2. Das Schlüsselwort
  3. verhindert das Zwischenspeichern der Daten im Thread.

Ich habe einen kleinen Test gemacht ........

  1. Ich habe eine ganzzahlige Variable mit dem Namen count verwendet und dabei ein flüchtiges Schlüsselwort verwendet.

  2. Dann wurden zwei verschiedene Threads erstellt, um den Variablenwert auf 10000 zu erhöhen, sodass das Endresultat 20000 sein sollte.

  3. Aber das ist nicht immer der Fall, mit volatilen Schlüsselwörtern bekomme ich nicht konstant 20000, sondern 18534, 15000, etc .... und manchmal 20000.

  4. Aber während ich ein synchronisiertes Keyword verwendet habe, hat es einfach funktioniert, warum .... ??

Kann mir bitte jemand dieses Verhalten des flüchtigen Keywords erklären.

Ich poste meinen Code mit flüchtigem Schlüsselwort und ebenso den Schlüssel mit synchronisiertem Schlüsselwort.

Der folgende Code verhält sich inkonsistent mit dem volatilen Schlüsselwort in der Variablenanzahl

%Vor%

Der folgende Code verhält sich perfekt mit synchronisiertem Schlüsselwort in der Methode go ().

%Vor%     
Kumar Vivek Mitra 01.07.2012, 10:49
quelle

4 Antworten

7

Das Schlüsselwort volatile ist nicht ein Synchronisationsgrundelement. Es verhindert lediglich das Zwischenspeichern des Werts für den Thread, verhindert jedoch nicht, dass zwei Threads denselben Wert ändern und gleichzeitig zurückschreiben.

Nehmen wir an, zwei Threads kommen an den Punkt, an dem sie den Zähler erhöhen müssen, der jetzt auf 5 gesetzt ist. Beide Threads sehen 5, machen 6 daraus und schreiben sie zurück in den Counter. Wenn der Zähler nicht volatile wäre, hätten beide Threads annehmen können, dass sie wissen, dass der Wert 6 ist, und überspringen den nächsten Lesevorgang. Es ist jedoch volatil, also würden beide 6 zurück lesen und weiter erhöhen. Da die Threads nicht in Lock-Step gehen, können Sie in der Ausgabe einen anderen Wert als 10000 sehen, aber es gibt praktisch keine Chance, dass Sie 20000 sehen.

    
dasblinkenlight 01.07.2012, 10:54
quelle
12

count = count + 1 ist nicht atomar. Es hat drei Schritte:

  1. lies den aktuellen Wert der Variablen
  2. erhöht den Wert
  3. schreibe den neuen Wert zurück in die Variable

Diese drei Schritte werden miteinander verwoben, was zu unterschiedlichen Ausführungspfaden führt, die zu einem falschen Wert führen. Verwenden Sie AtomicInteger.incrementAndGet() stattdessen, wenn Sie das synchronisierte Schlüsselwort vermeiden möchten.

Obwohl das volatile Keyword so funktioniert, wie Sie es beschrieben haben, gilt das nur für jede einzelne Operation. nicht zu allen drei Operationen zusammen.

    
Adam 01.07.2012 10:54
quelle
4

Die Tatsache, dass eine Variable volatile ist, bedeutet nicht, dass jede Operation, an der sie beteiligt ist, atomar ist. Zum Beispiel, diese Zeile in SynVsVol.Go :

%Vor%

wird zuerst count lesen, dann inkrementieren und das Ergebnis wird dann zurückgeschrieben. Wenn ein anderer Thread es gleichzeitig ausführt, hängen die Ergebnisse von der Verschachtelung der Befehle ab.

Wenn Sie nun syncronized hinzufügen, wird SynVsVol.Go automatisch ausgeführt. Das Inkrement wird nämlich als Ganzes von einem einzelnen Thread ausgeführt, und das andere kann count nicht ändern, bis es fertig ist.

Schließlich ist das Zwischenspeichern von Elementvariablen, die nur innerhalb eines synchronisierten Blocks modifiziert werden, viel einfacher. Der Compiler kann seinen Wert bei der Erfassung des Monitors lesen, ihn in einem Register zwischenspeichern, alle Änderungen in diesem Register vornehmen und ihn schließlich bei der Freigabe des Monitors in den Hauptspeicher zurückspülen. Dies ist auch der Fall, wenn Sie wait in einem synchronisierten Block aufrufen, und wenn ein anderer Thread notify s Sie: zwischengespeicherte Member-Variablen werden synchronisiert, und Ihr Programm wird kohärent bleiben. Das ist garantiert , selbst wenn die Elementvariable nicht deklariert ist als flüchtig:

  

Die Synchronisation stellt sicher, dass der Speicher vor oder nach einem Thread schreibt   während eines synchronisierten Blocks werden in einer vorhersagbaren Weise sichtbar gemacht   zu anderen Threads, die auf demselben Monitor synchronisieren.

    
eran 01.07.2012 10:53
quelle
3

Ihr Code ist fehlerhaft, weil er die Lese- und Inkrementierungsoperation für volatile als atomar behandelt, was nicht der Fall ist. Der Code enthält kein Datenrennen, enthält jedoch eine Racebedingung für int .

    
Marko Topolnik 01.07.2012 10:53
quelle