Ich suchte nach einer differenzierteren Antwort auf: "Was sind die Grenzen der einzelnen?". Wenn beispielsweise ein Flag von einem Thread gesetzt und von einem oder mehreren anderen gelesen wird, ist AtomicBoolean nicht erforderlich. Wie ich jedoch bei diesen Antworten sehe, bringt AtomicBoolean nicht sperrende Operationen vom Typ CAS ins Spiel, wenn Threads eine Variable in mehreren Threads gemeinsam nutzen, die schreiben können und auf das Ergebnis ihrer Lesevorgänge reagieren. Eigentlich lerne ich hier ziemlich viel. Hoffentlich werden auch andere davon profitieren.
Der flüchtige Boolesche Wert muss explizit synchronisiert werden, um die Race-Bedingungen zu bewältigen. Mit anderen Worten, ein Szenario wie die Aktualisierung der gemeinsam genutzten Ressource (Statusänderung) durch mehrere Threads, z. B. Inkrementierungs- / Dekrementierungszähler oder Flip-Boolescher Wert.
Sie sind einfach ganz anders. Betrachten Sie dieses Beispiel einer volatileGanzzahl:
volatileint i =0;void incIBy5(){
i +=5;}
Wenn zwei Threads die Funktion gleichzeitig aufrufen, ikann dies danach 5 sein, da der kompilierte Code diesem etwas ähnlich ist (außer Sie können nicht synchronisieren int):
void incIBy5(){int temp;synchronized(i){ temp = i }synchronized(i){ i = temp +5}}
Wenn eine Variable flüchtig ist, wird jeder atomare Zugriff darauf synchronisiert, aber es ist nicht immer offensichtlich, was tatsächlich als atomarer Zugriff qualifiziert ist. Mit einem Atomic*Objekt wird garantiert, dass jede Methode "atomar" ist.
Wenn Sie also ein AtomicIntegerund verwenden getAndAdd(int delta), können Sie sicher sein, dass das Ergebnis angezeigt wird 10. Auf die gleiche Weise können Sie sicher sein , dass zwei Threads gleichzeitig eine booleanVariable negieren. Mit a AtomicBooleankönnen Sie sicher sein, dass sie danach den ursprünglichen Wert hat. Mit a volatile booleankönnen Sie dies nicht.
Wenn also mehr als ein Thread ein Feld ändert, müssen Sie es atomar machen oder eine explizite Synchronisation verwenden.
Der Zweck von volatileist ein anderer. Betrachten Sie dieses Beispiel
Wenn ein Thread ausgeführt wird loop()und ein anderer Thread aufgerufen wird stop(), kann es zu einer Endlosschleife kommen, wenn Sie dies weglassen volatile, da der erste Thread möglicherweise den Wert von stop zwischenspeichert. Hier volatiledient das als Hinweis für den Compiler, bei Optimierungen etwas vorsichtiger zu sein.
-1: Sie geben Beispiele, erklären aber nicht wirklich den Unterschied zwischen einem flüchtigen und einem Atomicxxxx.
Jason S
70
Die Frage geht nicht um volatile. Die Frage ist etwa volatile booleanvs AtomicBoolean.
Dolmen
26
-1: Die speziell gestellte Frage zum Booleschen Wert ist ein eindeutiger Fall im Vergleich zu den anderen Datentypen und sollte direkt erläutert werden.
John Haager
8
@ sgp15 Es hat mit Synchronisation ab Java 5 zu tun.
Man of One Way
6
Wenn der boolesche Wert von vielen Threads gelesen, aber nur von einem Thread geschrieben wird, volatile booleanist dies ausreichend. Wenn es auch viele Schriftsteller gibt, brauchen Sie möglicherweise AtomicBoolean.
StvnBrkdll
263
Ich verwende flüchtige Felder, wenn dieses Feld NUR von seinem Eigentümer-Thread AKTUALISIERT wird und der Wert nur von anderen Threads gelesen wird. Sie können sich das als Publish / Subscribe-Szenario vorstellen, in dem es viele Beobachter, aber nur einen Publisher gibt. Wenn diese Beobachter jedoch eine Logik ausführen müssen, die auf dem Wert des Feldes basiert, und dann einen neuen Wert zurückschieben, dann gehe ich mit Atomic * vars oder Sperren oder synchronisierten Blöcken, was auch immer am besten zu mir passt. In vielen gleichzeitigen Szenarien läuft es darauf hinaus, den Wert abzurufen, mit einem anderen zu vergleichen und bei Bedarf zu aktualisieren, daher die in den Atomic * -Klassen vorhandenen Methoden compareAndSet und getAndSet.
In den JavaDocs des Pakets java.util.concurrent.atomic finden Sie eine Liste der Atomic-Klassen und eine hervorragende Erklärung ihrer Funktionsweise (Sie haben gerade erfahren, dass sie sperrenfrei sind, sodass sie einen Vorteil gegenüber Sperren oder synchronisierten Blöcken haben).
Dies ist wahr, aber wäre dies nicht eine ziemlich seltene Voraussetzung für einen Booleschen Wert?
Robin
1
@Robin überlegt, ob Sie damit einen verzögerten Aufruf einer Initialisierungsmethode steuern können.
Ustaman Sangat
Eigentlich würde ich denken, dass dies einer der Hauptanwendungsfälle ist.
narr4jesus
42
AtomicBooleanhat Methoden, die ihre zusammengesetzten Operationen atomar und ohne Verwendung eines synchronizedBlocks ausführen . Auf der anderen Seite volatile booleankönnen zusammengesetzte Operationen nur ausgeführt werden, wenn dies innerhalb eines synchronizedBlocks erfolgt.
Die Memory-Effekte beim Lesen / Schreiben volatile booleansind identisch mit denen getbzw. setMethoden von AtomicBoolean.
Zum Beispiel führt die compareAndSetMethode atomar Folgendes aus (ohne synchronizedBlock):
if(value == expectedValue){
value = newValue;returntrue;}else{returnfalse;}
Daher können Sie mit dieser compareAndSetMethode Code schreiben, der garantiert nur einmal ausgeführt wird, selbst wenn er von mehreren Threads aufgerufen wird. Beispielsweise:
Es wird garantiert, dass der Listener nur einmal benachrichtigt wird (vorausgesetzt, kein anderer Thread setzt den AtomicBooleanBack falsewieder auf, nachdem er auf gesetzt wurde true).
volatileDas Schlüsselwort garantiert, dass die Beziehung zwischen Threads, die diese Variable gemeinsam nutzen, erfolgt. Es garantiert Ihnen nicht, dass sich zwei oder mehr Threads beim Zugriff auf diese boolesche Variable nicht gegenseitig unterbrechen.
Der boolesche Zugriff (wie beim primitiven Typ) ist in Java atomar. Sowohl liest als auch Zuweisungen. Kein anderer Thread "unterbricht" also boolesche Operationen.
Maciej Biłas
1
Es tut mir leid, aber wie beantwortet dies die Frage? Eine Atomic*Klasse umschließt ein volatileFeld.
Grau
Sind die CPU-Caches nicht der Hauptfaktor für die Einstellung der Volatilität? Um sicherzustellen, dass der gelesene Wert tatsächlich dem entspricht, auf den er zuletzt eingestellt wurde
jocull
8
Volatile Boolean vs AtomicBoolean
Die Atomic * -Klassen umschließen ein flüchtiges Grundelement desselben Typs. Aus der Quelle:
publicclassAtomicLongextendsNumberimplements java.io.Serializable{...privatevolatilelong value;...publicfinallong get(){return value;}...publicfinalvoid set(long newValue){
value = newValue;}
Wenn Sie also nur ein Atomic * erhalten und einstellen, können Sie stattdessen auch ein flüchtiges Feld verwenden.
Was macht AtomicBoolean, was ein flüchtiger Boolescher Wert nicht erreichen kann?
Atomic * Klassen geben Ihnen Methoden , die erweiterte Funktionen bieten wie incrementAndGet(), compareAndSet()und andere , die mehrere Operationen (get / Zunahme / set, Test / set) ohne Verriegelung implementieren. Deshalb sind die Atomic * -Klassen so mächtig.
Wenn beispielsweise mehrere Threads den folgenden Code verwenden ++, gibt es Race-Bedingungen, da ++tatsächlich: get, inkrementieren und setzen.
privatevolatile value;...// race conditions here
value++;
Der folgende Code funktioniert jedoch in einer Multithread-Umgebung sicher ohne Sperren:
privatefinalAtomicLong value =newAtomicLong();...
value.incrementAndGet();
Es ist auch wichtig zu beachten, dass das Umschließen Ihres flüchtigen Felds mit der Atomic * -Klasse eine gute Möglichkeit ist, die kritische gemeinsam genutzte Ressource vom Standpunkt eines Objekts aus zu kapseln. Dies bedeutet, dass Entwickler nicht einfach mit dem Feld umgehen können, wenn es nicht gemeinsam genutzt wird, was möglicherweise Probleme mit einem Feld ++ verursacht. oder anderer Code, der Rennbedingungen einführt.
Der boolesche primitive Typ ist atomar für Schreib- und Leseoperationen, flüchtig garantiert das Vorher-Geschehen-Prinzip. Wenn Sie also ein einfaches get () und set () benötigen, benötigen Sie den AtomicBoolean nicht.
Wenn Sie jedoch vor dem Festlegen des Werts einer Variablen eine Prüfung durchführen müssen, z. B. "Wenn true, dann auf false setzen", müssen Sie diese Operation auch atomar ausführen. Verwenden Sie in diesem Fall compareAndSet und andere von bereitgestellte Methoden AtomicBoolean, da Sie, wenn Sie versuchen, diese Logik mit einem flüchtigen Booleschen Wert zu implementieren, eine Synchronisierung benötigen, um sicherzustellen, dass sich der Wert zwischen get und set nicht geändert hat.
Kurz, knusprig und auf den Punkt. volatileFunktioniert nur in den Fällen, in denen der Eigentümer-Thread den Feldwert aktualisieren kann und die anderen Threads nur lesen können.
Chaklader Asfak Arefe
3
Wenn Sie nur einen Thread haben, der Ihren Booleschen Wert ändert, können Sie einen flüchtigen Booleschen Wert verwenden (normalerweise definieren Sie damit eine stopVariable, die in der Hauptschleife des Threads überprüft wird).
Wenn Sie jedoch mehrere Threads haben, die den Booleschen Wert ändern, sollten Sie einen verwenden AtomicBoolean. Andernfalls ist der folgende Code nicht sicher:
boolean r =!myVolatileBoolean;
Dieser Vorgang erfolgt in zwei Schritten:
Der boolesche Wert wird gelesen.
Der boolesche Wert wird geschrieben.
Wenn ein anderer Thread den Wert zwischen #1und ändert 2#, wird möglicherweise ein falsches Ergebnis angezeigt. AtomicBooleanMethoden vermeiden dieses Problem, indem sie Schritte #1und #2atomar ausführen.
Antworten:
Sie sind einfach ganz anders. Betrachten Sie dieses Beispiel einer
volatile
Ganzzahl:Wenn zwei Threads die Funktion gleichzeitig aufrufen,
i
kann dies danach 5 sein, da der kompilierte Code diesem etwas ähnlich ist (außer Sie können nicht synchronisierenint
):Wenn eine Variable flüchtig ist, wird jeder atomare Zugriff darauf synchronisiert, aber es ist nicht immer offensichtlich, was tatsächlich als atomarer Zugriff qualifiziert ist. Mit einem
Atomic*
Objekt wird garantiert, dass jede Methode "atomar" ist.Wenn Sie also ein
AtomicInteger
und verwendengetAndAdd(int delta)
, können Sie sicher sein, dass das Ergebnis angezeigt wird10
. Auf die gleiche Weise können Sie sicher sein , dass zwei Threads gleichzeitig eineboolean
Variable negieren. Mit aAtomicBoolean
können Sie sicher sein, dass sie danach den ursprünglichen Wert hat. Mit avolatile boolean
können Sie dies nicht.Wenn also mehr als ein Thread ein Feld ändert, müssen Sie es atomar machen oder eine explizite Synchronisation verwenden.
Der Zweck von
volatile
ist ein anderer. Betrachten Sie dieses BeispielWenn ein Thread ausgeführt wird
loop()
und ein anderer Thread aufgerufen wirdstop()
, kann es zu einer Endlosschleife kommen, wenn Sie dies weglassenvolatile
, da der erste Thread möglicherweise den Wert von stop zwischenspeichert. Hiervolatile
dient das als Hinweis für den Compiler, bei Optimierungen etwas vorsichtiger zu sein.quelle
volatile
. Die Frage ist etwavolatile boolean
vsAtomicBoolean
.volatile boolean
ist dies ausreichend. Wenn es auch viele Schriftsteller gibt, brauchen Sie möglicherweiseAtomicBoolean
.Ich verwende flüchtige Felder, wenn dieses Feld NUR von seinem Eigentümer-Thread AKTUALISIERT wird und der Wert nur von anderen Threads gelesen wird. Sie können sich das als Publish / Subscribe-Szenario vorstellen, in dem es viele Beobachter, aber nur einen Publisher gibt. Wenn diese Beobachter jedoch eine Logik ausführen müssen, die auf dem Wert des Feldes basiert, und dann einen neuen Wert zurückschieben, dann gehe ich mit Atomic * vars oder Sperren oder synchronisierten Blöcken, was auch immer am besten zu mir passt. In vielen gleichzeitigen Szenarien läuft es darauf hinaus, den Wert abzurufen, mit einem anderen zu vergleichen und bei Bedarf zu aktualisieren, daher die in den Atomic * -Klassen vorhandenen Methoden compareAndSet und getAndSet.
In den JavaDocs des Pakets java.util.concurrent.atomic finden Sie eine Liste der Atomic-Klassen und eine hervorragende Erklärung ihrer Funktionsweise (Sie haben gerade erfahren, dass sie sperrenfrei sind, sodass sie einen Vorteil gegenüber Sperren oder synchronisierten Blöcken haben).
quelle
boolean
wir wählen sollten , wenn nur ein Thread die Variable ändertvolatile boolean
.Sie können nicht tun
compareAndSet
,getAndSet
als atomare Operation mit flüchtigen boolean (es sei denn natürlich synchronisieren Sie es).quelle
AtomicBoolean
hat Methoden, die ihre zusammengesetzten Operationen atomar und ohne Verwendung einessynchronized
Blocks ausführen . Auf der anderen Seitevolatile boolean
können zusammengesetzte Operationen nur ausgeführt werden, wenn dies innerhalb einessynchronized
Blocks erfolgt.Die Memory-Effekte beim Lesen / Schreiben
volatile boolean
sind identisch mit denenget
bzw.set
Methoden vonAtomicBoolean
.Zum Beispiel führt die
compareAndSet
Methode atomar Folgendes aus (ohnesynchronized
Block):Daher können Sie mit dieser
compareAndSet
Methode Code schreiben, der garantiert nur einmal ausgeführt wird, selbst wenn er von mehreren Threads aufgerufen wird. Beispielsweise:Es wird garantiert, dass der Listener nur einmal benachrichtigt wird (vorausgesetzt, kein anderer Thread setzt den
AtomicBoolean
Backfalse
wieder auf, nachdem er auf gesetzt wurdetrue
).quelle
volatile
Das Schlüsselwort garantiert, dass die Beziehung zwischen Threads, die diese Variable gemeinsam nutzen, erfolgt. Es garantiert Ihnen nicht, dass sich zwei oder mehr Threads beim Zugriff auf diese boolesche Variable nicht gegenseitig unterbrechen.quelle
Atomic*
Klasse umschließt einvolatile
Feld.Die Atomic * -Klassen umschließen ein flüchtiges Grundelement desselben Typs. Aus der Quelle:
Wenn Sie also nur ein Atomic * erhalten und einstellen, können Sie stattdessen auch ein flüchtiges Feld verwenden.
Atomic * Klassen geben Ihnen Methoden , die erweiterte Funktionen bieten wie
incrementAndGet()
,compareAndSet()
und andere , die mehrere Operationen (get / Zunahme / set, Test / set) ohne Verriegelung implementieren. Deshalb sind die Atomic * -Klassen so mächtig.Wenn beispielsweise mehrere Threads den folgenden Code verwenden
++
, gibt es Race-Bedingungen, da++
tatsächlich: get, inkrementieren und setzen.Der folgende Code funktioniert jedoch in einer Multithread-Umgebung sicher ohne Sperren:
Es ist auch wichtig zu beachten, dass das Umschließen Ihres flüchtigen Felds mit der Atomic * -Klasse eine gute Möglichkeit ist, die kritische gemeinsam genutzte Ressource vom Standpunkt eines Objekts aus zu kapseln. Dies bedeutet, dass Entwickler nicht einfach mit dem Feld umgehen können, wenn es nicht gemeinsam genutzt wird, was möglicherweise Probleme mit einem Feld ++ verursacht. oder anderer Code, der Rennbedingungen einführt.
quelle
Wenn mehrere Threads auf Variablen auf Klassenebene zugreifen, kann jeder Thread eine Kopie dieser Variablen in seinem threadlokalen Cache behalten.
Wenn Sie die Variable flüchtig machen, wird verhindert, dass Threads die Kopie der Variablen im threadlokalen Cache behalten.
Atomvariablen sind unterschiedlich und ermöglichen eine atomare Änderung ihrer Werte.
quelle
Der boolesche primitive Typ ist atomar für Schreib- und Leseoperationen, flüchtig garantiert das Vorher-Geschehen-Prinzip. Wenn Sie also ein einfaches get () und set () benötigen, benötigen Sie den AtomicBoolean nicht.
Wenn Sie jedoch vor dem Festlegen des Werts einer Variablen eine Prüfung durchführen müssen, z. B. "Wenn true, dann auf false setzen", müssen Sie diese Operation auch atomar ausführen. Verwenden Sie in diesem Fall compareAndSet und andere von bereitgestellte Methoden AtomicBoolean, da Sie, wenn Sie versuchen, diese Logik mit einem flüchtigen Booleschen Wert zu implementieren, eine Synchronisierung benötigen, um sicherzustellen, dass sich der Wert zwischen get und set nicht geändert hat.
quelle
Erinnern Sie sich an das IDIOM -
READ - MODIFY- WRITE Dies können Sie mit volatile nicht erreichen
quelle
volatile
Funktioniert nur in den Fällen, in denen der Eigentümer-Thread den Feldwert aktualisieren kann und die anderen Threads nur lesen können.Wenn Sie nur einen Thread haben, der Ihren Booleschen Wert ändert, können Sie einen flüchtigen Booleschen Wert verwenden (normalerweise definieren Sie damit eine
stop
Variable, die in der Hauptschleife des Threads überprüft wird).Wenn Sie jedoch mehrere Threads haben, die den Booleschen Wert ändern, sollten Sie einen verwenden
AtomicBoolean
. Andernfalls ist der folgende Code nicht sicher:Dieser Vorgang erfolgt in zwei Schritten:
Wenn ein anderer Thread den Wert zwischen
#1
und ändert2#
, wird möglicherweise ein falsches Ergebnis angezeigt.AtomicBoolean
Methoden vermeiden dieses Problem, indem sie Schritte#1
und#2
atomar ausführen.quelle
Beide haben das gleiche Konzept, aber im atomaren Booleschen Wert wird die Operation atomisiert, falls der CPU-Schalter dazwischen liegt.
quelle