Dieser Artikel analysiert die Konzepte, Prinzipien und Verwendung von Cas-Operationen in der Java-Programmierung anhand von Beispielen. Er hat einen gewissen Referenzwert und Freunde in Not können mehr darüber erfahren.
CAS bezieht sich auf eine spezielle Anweisung, die von modernen CPUs weithin unterstützt wird und mit gemeinsam genutzten Daten im Speicher arbeitet. Diese Anweisung führt atomare Lese- und Schreibvorgänge für gemeinsam genutzte Daten im Speicher aus.
Eine kurze Einführung in den Ablauf dieser Anweisung: Zunächst vergleicht die CPU die zu ändernden Daten im Speicher mit dem erwarteten Wert. Wenn dann die beiden Werte gleich sind, ersetzt die CPU den Wert im Speicher durch den neuen Wert. Andernfalls wird kein Vorgang ausgeführt. Schließlich gibt die CPU den alten Wert zurück. Diese Reihe von Operationen ist atomar. Obwohl sie kompliziert erscheinen, sind sie der Hauptgrund dafür, dass der Parallelitätsmechanismus von Java 5 besser ist als der ursprüngliche Sperrmechanismus. Einfach ausgedrückt lautet die Bedeutung von CAS: „Was sollte meiner Meinung nach der ursprüngliche Wert sein? Wenn ja, aktualisieren Sie den ursprünglichen Wert auf den neuen Wert, andernfalls ändern Sie ihn nicht und sagen Sie mir, wie hoch der ursprüngliche Wert ist.“ (Diese Beschreibung stammt aus „Java Concurrent Programming Practice“)
Einfach ausgedrückt verfügt CAS über drei Operanden: den Speicherwert V, den alten erwarteten Wert A und den neuen zu ändernden Wert B . Nur wenn der erwartete Wert A und der Speicherwert V gleich sind, ändern Sie den Speicherwert V in B, andernfalls geben Sie V zurück. Dies ist eine optimistische Sperridee. Es wird angenommen, dass kein anderer Thread sie ändern wird, bevor sie geändert wird. Es wird davon ausgegangen, dass andere Threads sie ändern werden, bevor sie geändert wird.
Sehen wir uns ein einfaches Beispiel an:
if(a==b) { a++; }
Stellen Sie sich vor, was wäre, wenn der Wert von a geändert würde, bevor a++ ausgeführt würde? Wird a++ noch ausgeführt? Der Grund für dieses Problem liegt darin, dass sich der Wert von a in einer Multithread-Umgebung in einem unsicheren Zustand befindet. Sperren können solche Probleme lösen, CAS kann sie aber auch ohne Sperren lösen.
int expect = a; if(a.compareAndSet(expect,a+1)) { doSomeThing1(); } else { doSomeThing2(); }
Auf diese Weise wird a++ nicht ausgeführt, wenn der Wert von a geändert wird.
Gemäß der obigen Schreibmethode wird a++ nicht ausgeführt. Was ist, wenn wir immer noch eine ++-Operation ausführen möchten? 🎜>
while(true) { int expect = a; if (a.compareAndSet(expect, a + 1)) { doSomeThing1(); return; } else { doSomeThing2(); } }Mit der oben genannten Schreibmethode wird eine ++-Operation ohne Sperre implementiert. Dies ist tatsächlich ein nicht blockierender Algorithmus.
Anwendung
Fast die meisten Klassen im Paket java.util.concurrent.atomic verwenden CAS-Operationen, nehmen Sie AtomicInteger als Beispiel, siehe Look bei der Implementierung mehrerer seiner Hauptmethoden:public final int getAndSet(int newValue) { for (;;) { int current = get(); if (compareAndSet(current, newValue)) return current; } }Die Erklärung in der JDK-Dokumentation zur getAndSet-Methode lautet: atomar auf einen bestimmten Wert setzen und alt zurückgeben Wert. Wo sich die atomare Methode widerspiegelt, spiegelt sie sich in CompareAndSet wider. Sehen wir uns an, wie CompareAndSet implementiert wird:
public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }Wie erwartet wird die Unsafe-Klasse verwendet. Die CAS-Operation ist vollendet.
public final int getAndIncrement() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return current; } }Es ist fast genau dasselbe wie das ursprüngliche Beispiel. und verwendet auch die CAS-Operation, um die automatische Inkrementierung zu implementieren.
public final int incrementAndGet() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return next; } }Außerdem java.util .concurrent. Die ConcurrentLinkedQueue-Klasse verwendet einen nicht blockierenden Algorithmus, der keine Sperren verwendet und vollständig auf CAS-Operationen basiert. Man kann sagen, dass der CAS-Betrieb die Grundlage des JAVA-Parallelitäts-Frameworks ist, und das Design des gesamten Frameworks basiert auf dem CAS-Betrieb.
Nachteile:
Zusammenfassung
CAS kann verwendet werden, um atomare Operationen ohne Sperren zu implementieren, aber die Anwendungsszenarien müssen klar definiert sein. Für sehr einfache Operationen ohne Einführung von Sperren können Sie die Verwendung von CAS-Operationen in Betracht ziehen, wenn Sie eine durchführen möchten Betrieb nicht blockierend. Es wird nicht empfohlen, CAS bei komplexen Vorgängen einzuführen, da das Programm dadurch weniger lesbar und schwieriger zu testen ist und ABA-Probleme auftreten.
Das obige ist der detaillierte Inhalt vonAnalyse von Cas-Operationen in Java. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!