volatile ist ein Feldmodifikator, während synchronized Codeblöcke und Methoden modifiziert. Daher können wir drei Variationen eines einfachen Zugriffsmethode unter Verwendung dieser beiden Schlüsselwörter angeben:
int i1;
int geti1() {return i1;}
volatile int i2;
int geti2() {return i2;}
int i3;
synchronized int geti3() {return i3;}
geti1()
greift auf den aktuell in i1
im aktuellen Thread gespeicherten Wert zu. Threads können lokale Kopien von Variablen haben, und die Daten müssen nicht mit den Daten in anderen Threads übereinstimmen. Insbesondere ein anderer Thread kann i1
in seinem Thread aktualisiert haben, aber der Wert im aktuellen Thread könnte sich von diesem aktualisierten Wert unterscheiden. Tatsächlich hat Java die Idee eines "Haupt"-Speichers, der den aktuellen "korrekten" Wert für Variablen speichert. Threads können ihre eigene Kopie von Daten für Variablen haben, und die Thread-Kopie kann sich vom "Haupt"-Speicher unterscheiden. Tatsächlich ist es also möglich, dass der "Haupt"-Speicher einen Wert von 1 für i1
hat, Thread1 einen Wert von 2 für i1
hat und Thread2 einen Wert von 3 für i1
hat, wenn Thread1 und Thread2 beide i1 aktualisiert haben, aber diese aktualisierten Werte noch nicht im "Haupt"-Speicher oder anderen Threads propagiert wurden.
Andererseits greift geti2()
effektiv auf den Wert von i2
im "Haupt"-Speicher zu. Eine volatile Variable darf keine lokale Kopie einer Variablen haben, die sich vom aktuell im "Haupt"-Speicher gehaltenen Wert unterscheidet. Effektiv muss eine als volatile deklarierte Variable ihre Daten zwischen allen Threads synchronisieren, sodass immer, wenn Sie die Variable in einem Thread zugreifen oder aktualisieren, alle anderen Threads sofort den gleichen Wert sehen. Im Allgemeinen haben volatile Variablen einen höheren Zugriffs- und Aktualisierungsüberhead als "normale" Variablen. Normalerweise dürfen Threads ihre eigene Kopie von Daten haben, um die Effizienz zu steigern.
Es gibt zwei Unterschiede zwischen volitile und synchronized.
Erstens erwerben und lösen synchronized Sperren für Monitore, die nur einen Thread dazu zwingen können, einen Codeblock gleichzeitig auszuführen. Das ist der ziemlich bekannte Aspekt von synchronized. Aber synchronized synchronisiert auch den Speicher. Tatsächlich synchronisiert synchronized den gesamten Thread-Speicher mit dem "Haupt"-Speicher. Das Ausführen von geti3()
macht Folgendes:
- Der Thread erwirbt die Sperre für den Monitor für das Objekt "this".
- Der Thread-Speicher leert alle seine Variablen, d.h. er liest effektiv alle seine Variablen aus dem "Haupt"-Speicher.
- Der Codeblock wird ausgeführt (in diesem Fall wird der Rückgabewert auf den aktuellen Wert von i3 gesetzt, der gerade aus dem "Haupt"-Speicher zurückgesetzt worden sein könnte).
- (Alle Änderungen an Variablen würden normalerweise jetzt in den "Haupt"-Speicher geschrieben, aber für geti3() haben wir keine Änderungen.)
- Der Thread gibt die Sperre für den Monitor für das Objekt "this" frei.
Während also volatile nur den Wert einer Variablen zwischen Thread-Speicher und "Haupt"-Speicher synchronisiert, synchronisierte den Wert aller Variablen zwischen Thread-Speicher und "Haupt"-Speicher und sperrt und löst einen Monitor. Offensichtlich hat synchronized wahrscheinlich mehr Overhead als volatile.
http://javaexp.blogspot.com/2007/12/difference-between-volatile-and.html
0 Stimmen
Um meinen automatischen "Möglicherweise" -Duplikatkommentar zu klären: Die beiden vorherigen Vorschläge waren keine exakten Duplikate; meiner ist es. Es ist wichtig, dass diese Frage als Duplikat geklärt wird, da die ausgewählte Antwort auf diese Frage gefährlich falsch ist, wenn sie behauptet, dass volatil nur das als volatil deklarierte Feld betrifft - tatsächlich führt ein Schreib- und ein Lesebefehl von jeder volatilen Variable eine vollständige Speicherbarriere ein, genauso wie die Synchronisation zwischen zwei Threads.