Wann verwenden wir AtomicReference
?
Müssen Objekte in allen Multithread-Programmen erstellt werden?
Geben Sie ein einfaches Beispiel an, in dem AtomicReference verwendet werden soll.
quelle
Wann verwenden wir AtomicReference
?
Müssen Objekte in allen Multithread-Programmen erstellt werden?
Geben Sie ein einfaches Beispiel an, in dem AtomicReference verwendet werden soll.
Die atomare Referenz sollte in einer Umgebung verwendet werden, in der Sie einfache atomare (dh threadsichere , nicht triviale) Operationen an einer Referenz ausführen müssen, für die eine überwacherbasierte Synchronisation nicht geeignet ist. Angenommen, Sie möchten nur dann überprüfen, ob ein bestimmtes Feld vorhanden ist, wenn der Status des Objekts so bleibt, wie Sie es zuletzt überprüft haben:
AtomicReference<Object> cache = new AtomicReference<Object>();
Object cachedValue = new Object();
cache.set(cachedValue);
//... time passes ...
Object cachedValueToUpdate = cache.get();
//... do some work to transform cachedValueToUpdate into a new version
Object newValue = someFunctionOfOld(cachedValueToUpdate);
boolean success = cache.compareAndSet(cachedValue,cachedValueToUpdate);
Aufgrund der atomaren Referenzsemantik können Sie dies auch dann tun, wenn das cache
Objekt von Threads gemeinsam genutzt wird, ohne es zu verwenden synchronized
. Im Allgemeinen ist es besser, Synchronizer oder das java.util.concurrent
Framework zu verwenden, als nackt zu sein, es Atomic*
sei denn, Sie wissen, was Sie tun.
Zwei ausgezeichnete Dead-Tree-Referenzen, die Sie in dieses Thema einführen:
Beachten Sie, dass die Referenzzuweisung (dh ich weiß nicht, ob dies immer der Fall war) =
selbst atomar ist (Aktualisieren primitiver 64-Bit-Typen wie long
oder double
möglicherweise nicht atomar ist; das Aktualisieren einer Referenz jedoch immer atomar ist, selbst wenn es 64-Bit ist ) ohne explizite Verwendung eines Atomic*
.
Siehe die Java-Sprachspezifikation 3ed, Abschnitt 17.7 .
AtomicReference
, sollten Sie die Variable markieren,volatile
da die Laufzeit zwar garantiert, dass die Referenzzuweisung atomar ist, der Compiler jedoch Optimierungen unter der Annahme durchführen kann, dass die Variable nicht von anderen Threads geändert wurde.AtomicReference
"; Wenn Sie es verwenden, würde ich raten, in die entgegengesetzte Richtung zu gehen und es zu markieren,final
damit der Compiler entsprechend optimieren kann.Eine atomare Referenz ist ideal, wenn Sie den Status eines unveränderlichen Objekts zwischen mehreren Threads teilen und ändern müssen. Das ist eine super dichte Aussage, also werde ich sie ein bisschen aufschlüsseln.
Erstens ist ein unveränderliches Objekt ein Objekt, das nach der Konstruktion effektiv nicht verändert wird. Häufig geben die Methoden eines unveränderlichen Objekts neue Instanzen derselben Klasse zurück. Einige Beispiele sind die Wrapper-Klassen Long und Double sowie String, um nur einige zu nennen. (Gemäß Programming Concurrency in der JVM unveränderliche Objekte ein kritischer Bestandteil der modernen Parallelität).
Warum ist AtomicReference besser als ein flüchtiges Objekt, um diesen gemeinsamen Wert zu teilen? Ein einfaches Codebeispiel zeigt den Unterschied.
Jedes Mal, wenn Sie die Zeichenfolge, auf die dieses flüchtige Feld verweist, basierend auf dem aktuellen Wert ändern möchten, müssen Sie zuerst eine Sperre für dieses Objekt erhalten. Dadurch wird verhindert, dass in der Zwischenzeit ein anderer Thread eingeht und der Wert in der Mitte der neuen Zeichenfolgenverkettung geändert wird. Wenn Ihr Thread fortgesetzt wird, blockieren Sie die Arbeit des anderen Threads. Aber ehrlich gesagt wird dieser Code funktionieren, er sieht sauber aus und würde die meisten Menschen glücklich machen.
Kleines Problem. Es ist langsam. Vor allem, wenn es viele Konflikte mit diesem Sperrobjekt gibt. Dies liegt daran, dass die meisten Sperren einen Systemaufruf des Betriebssystems erfordern und Ihr Thread blockiert und aus der CPU kontextgeschaltet wird, um Platz für andere Prozesse zu schaffen.
Die andere Option ist die Verwendung einer AtomicRefrence.
Warum ist das besser? Ehrlich gesagt ist dieser Code etwas weniger sauber als zuvor. Aber in AtomicRefrence passiert etwas wirklich Wichtiges unter der Haube, nämlich Vergleichen und Tauschen. Es ist ein einzelner CPU-Befehl, kein Betriebssystemaufruf, der den Wechsel ermöglicht. Das ist eine einzelne Anweisung auf der CPU. Und weil es keine Sperren gibt, gibt es keinen Kontextwechsel für den Fall, dass die Sperre ausgeübt wird, was noch mehr Zeit spart!
Der Haken ist, dass für AtomicReferences kein .equals () -Aufruf verwendet wird, sondern ein == -Vergleich für den erwarteten Wert. Stellen Sie also sicher, dass das erwartete Objekt das tatsächliche Objekt ist, das von get in the loop zurückgegeben wird.
quelle
worked
, um die gleiche Semantik zu erhalten.Hier ist ein Anwendungsfall für AtomicReference:
Betrachten Sie diese Klasse, die als Zahlenbereich fungiert und einzelne AtmomicInteger-Variablen verwendet, um die unteren und oberen Zahlengrenzen beizubehalten.
Sowohl setLower als auch setUpper sind Check-Then-Act-Sequenzen, verwenden jedoch nicht genügend Sperren, um sie atomar zu machen. Wenn der Nummernkreis gilt (0, 10) und ein Thread setLower (5) aufruft, während ein anderer Thread setUpper (4) aufruft, bestehen beide mit etwas unglücklichem Timing die Prüfungen in den Setzern und beide Modifikationen werden angewendet. Das Ergebnis ist, dass der Bereich jetzt (5, 4) einen ungültigen Zustand enthält. Während die zugrunde liegenden AtomicIntegers threadsicher sind, ist dies bei der zusammengesetzten Klasse nicht der Fall. Dies kann mithilfe einer AtomicReference behoben werden, anstatt einzelne AtomicIntegers für die oberen und unteren Grenzen zu verwenden.
quelle
Sie können AtomicReference verwenden, wenn Sie optimistische Sperren anwenden. Sie haben ein freigegebenes Objekt und möchten es von mehr als einem Thread ändern.
Da andere Thread es möglicherweise geändert haben und / oder zwischen diesen beiden Schritten ändern können. Sie müssen es in einer atomaren Operation tun. Hier kann AtomicReference helfen
quelle
Hier ist ein sehr einfacher Anwendungsfall, der nichts mit der Gewindesicherheit zu tun hat.
Um ein Objekt zwischen Lambda-Aufrufen zu teilen,
AtomicReference
ist dies eine Option :Ich sage nicht, dass dies gutes Design ist oder so (es ist nur ein triviales Beispiel), aber wenn Sie den Fall haben, dass Sie ein Objekt zwischen Lambda-Aufrufen teilen müssen, ist das
AtomicReference
ist dies eine Option.Tatsächlich können Sie jedes Objekt verwenden, das eine Referenz enthält, auch eine Sammlung mit nur einem Element. Die AtomicReference passt jedoch perfekt.
quelle
Ich werde nicht viel reden. Meine angesehenen Freunde haben bereits wertvolle Beiträge geleistet. Der vollwertige Laufcode am Ende dieses Blogs sollte jegliche Verwirrung beseitigen. Es geht um ein kleines Programm für die Buchung eines Kinositzes in einem Multithread-Szenario.
Einige wichtige elementare Tatsachen sind wie folgt. 1> Verschiedene Threads können nur mit statischen Elementvariablen im Heap-Bereich konkurrieren. 2> Flüchtiges Lesen oder Schreiben ist vollständig atomar und serialisiert / geschieht vorher und erfolgt nur aus dem Speicher. Damit meine ich, dass jeder Lesevorgang dem vorherigen Schreibvorgang im Speicher folgt. Und jedes Schreiben folgt dem vorherigen Lesen aus dem Speicher. Jeder Thread, der mit einem flüchtigen Thread arbeitet, sieht also immer den aktuellsten Wert. AtomicReference verwendet diese Eigenschaft von flüchtig.
Im Folgenden finden Sie einige Quellcodes von AtomicReference. AtomicReference bezieht sich auf eine Objektreferenz. Diese Referenz ist eine flüchtige Mitgliedsvariable in der AtomicReference-Instanz (siehe unten).
get () gibt einfach den neuesten Wert der Variablen zurück (wie es bei flüchtigen Stoffen der Fall ist).
Das Folgende ist die wichtigste Methode von AtomicReference.
Die Methode compareAndSet (erwarten, aktualisieren) ruft die Methode compareAndSwapObject () der unsicheren Java-Klasse auf. Dieser unsichere Methodenaufruf ruft den nativen Aufruf auf, der eine einzelne Anweisung an den Prozessor aufruft. "Erwarten" und "Aktualisieren" jeder Referenz eines Objekts.
Wenn und nur wenn die AtomicReference-Instanzmitgliedsvariable "value" auf dasselbe Objekt verweist, wird auf "expected" verwiesen, "update" wird jetzt dieser Instanzvariablen zugewiesen und "true" wird zurückgegeben. Andernfalls wird false zurückgegeben. Das Ganze wird atomar gemacht. Kein anderer Thread kann dazwischen abfangen. Da es sich um eine Einzelprozessoroperation handelt (Magie der modernen Computerarchitektur), ist sie häufig schneller als die Verwendung eines synchronisierten Blocks. Aber denken Sie daran , dass , wenn mehrere Variablen müssen atomar aktualisiert werden, AtomicReference wird nicht helfen.
Ich möchte einen vollwertigen laufenden Code hinzufügen, der in Eclipse ausgeführt werden kann. Es würde viele Verwirrung beseitigen. Hier versuchen 22 Benutzer (MyTh-Threads) 20 Plätze zu buchen. Es folgt das Code-Snippet, gefolgt vom vollständigen Code.
Code-Snippet, bei dem 22 Benutzer versuchen, 20 Plätze zu buchen.
Es folgt der vollständige laufende Code.
quelle
AtomicReference ist eine flexible Möglichkeit, den Variablenwert ohne Verwendung der Synchronisation atomar zu aktualisieren.
AtomicReference
Unterstützung der sperrenfreien threadsicheren Programmierung einzelner Variablen.Es gibt mehrere Möglichkeiten, die Thread-Sicherheit mit einer gleichzeitigen API auf hoher Ebene zu erreichen . Atomare Variablen sind eine der vielen Optionen.
Lock
Objekte unterstützen Sperrsprachen, die viele gleichzeitige Anwendungen vereinfachen.Executors
Definieren Sie eine allgemeine API zum Starten und Verwalten von Threads. Von java.util.concurrent bereitgestellte Executor-Implementierungen bieten eine Thread-Pool-Verwaltung, die für umfangreiche Anwendungen geeignet ist.Gleichzeitige Sammlungen erleichtern die Verwaltung großer Datensammlungen und können den Synchronisierungsbedarf erheblich reduzieren.
Atomare Variablen verfügen über Funktionen, die die Synchronisation minimieren und Speicherkonsistenzfehler vermeiden.
Beispielcode mit
AtomicReference
:Sie müssen nicht verwenden
AtomicReference
in allen Multithread-Programmen verwenden.Wenn Sie eine einzelne Variable schützen möchten, verwenden Sie
AtomicReference
. Wenn Sie einen Codeblock schützen möchten, verwenden Sie andere Konstrukte wieLock
/synchronized
usw.quelle
Ein weiteres einfaches Beispiel ist eine Safe-Thread-Änderung in einem Sitzungsobjekt.
Quelle: http://www.ibm.com/developerworks/library/j-jtp09238/index.html
quelle