Was ist ein Spin -Lock
Apropos Spinschlösser, wir müssen mit dem Multi-Threading-Mechanismus mit dem Schließmechanismus beginnen. Da einige Ressourcen in einer Umgebung mit mehreren Prozessoren begrenzt sind, erfordern sie manchmal einen gegenseitigen Ausschluss. Zu diesem Zeitpunkt wird ein Sperrmechanismus eingeführt. Nur der Prozess, der das Schloss erfasst, kann Ressourcenzugriff erhalten. Das heißt, nur ein Prozess kann das Schloss gleichzeitig erwerben, um einen eigenen kritischen Bereich zu betreten. Gleichzeitig können zwei oder mehr Prozesse den kritischen Bereich nicht betreten. Beim Verlassen des kritischen Bereichs wird das Schloss veröffentlicht.
Wenn Sie einen Mutex -Algorithmus entwerfen, stehen Sie immer einer Situation, in der Sie kein Schloss haben. Was sollten Sie tun, wenn Sie kein Schloss erhalten?
Es gibt normalerweise 2 Möglichkeiten, damit umzugehen:
Eine davon ist, dass der Anrufer, der das Schloss nicht erhalten hat, dort herumschleudert, um zu sehen, ob der Inhaber des Spin -Schlosses das Schloss freigegeben hat. Dies ist der Schwerpunkt dieses Artikels - Spin Lock. Er muss die Linienstadt nicht blockieren (nicht blockiert).
Eine andere Möglichkeit besteht darin, dass der Prozess ohne die Erhalt des Schlosses selbst blockiert (blockiert) und weiterhin andere Aufgaben auf dem Thread ausführt, der das Mutex ist (einschließlich der eingebauten Sperrsynchronisierung, Re-Entrantlock usw.).
Einführung
CAS (Compare and Swap), dh Vergleich und Austausch, ist auch die Kernoperation, die das implementiert, was wir normalerweise als Spin -Lock oder optimistisches Schloss bezeichnen.
Die Implementierung ist sehr einfach, nämlich einen erwarteten Wert mit einem Speicherwert zu vergleichen. Wenn die beiden Werte gleich sind, ersetzen Sie den Speicherwert durch den erwarteten Wert und geben Sie die true zurück. Ansonsten kehren Sie falsch zurück.
Gewährleistung des Atombetriebs
Jede Technologie entsteht, um bestimmte spezifische Probleme zu lösen. Das Problem, das CAS lösen muss, ist die Gewährleistung von atomaren Operationen. Was ist eine Atomoperation? Atome sind die kleinsten und unanständigen, und die Atomoperation ist die kleinste und unanständige Operation. Das heißt, sobald die Operation beginnt, kann sie nicht unterbrochen werden und weiß, dass die Operation abgeschlossen ist. In einer Multi-Threaden-Umgebung sind Atomoperationen ein wichtiges Mittel, um die Sicherheit der Fäden zu gewährleisten. Nehmen wir beispielsweise an, dass zwei Threads arbeiten und einen bestimmten Wert ändern möchten. Nehmen Sie den Selbststillstandsvorgang als Beispiel. Um einen Selbstunternehmensbetrieb in einer Ganzzahl I durchzuführen, sind drei grundlegende Schritte erforderlich:
1. Lesen Sie den aktuellen Wert von i;
2. Fügen Sie dem I -Wert 1 hinzu;
3. Schreiben Sie den I -Wert zurück in den Speicher;
Angenommen, beide Prozesse lesen den aktuellen Wert von i unter der Annahme, dass es 0 ist, fügt Thread A 1 zu I hinzu, Thread B fügt ebenfalls 1 hinzu, und schließlich ist ich 1, nicht 2. Dies liegt daran, dass die automatische Operation keine Atomoperation ist und die drei Schritte unterteilt werden können. Wie im folgenden Beispiel führt bei 10 Threads jeder Thread 10.000 i ++ - Operationen durch, der erwartete Wert beträgt 100.000, aber leider beträgt das Ergebnis immer weniger als 100.000.
statische int i = 0; public static void add () {i ++; } private statische Klasse plus implementiert runnable {@Override public void run () {für (int k = 0; k <10000; k ++) {add (); }}} public static void main (String [] args) löst unterbrochene Ausnahme {Thread [] threads = neuer Thread [10]; für (int i = 0; i <10; i ++) {threads [i] = neuer Thread (neu plus ()); Themen [i] .Start (); } für (int i = 0; i <10; i ++) {threads [i] .Join (); } System.out.println (i); }Was soll ich in diesem Fall tun? Das ist richtig, vielleicht haben Sie bereits darüber nachgedacht. Sie können synchronisierte Implementierung sperren oder verwenden, z. B. die add () -Methode in Folgendes ändern:
public synchronisierte statische Leere add () {i ++; }Alternativ wird der Sperrvorgang beispielsweise unter Verwendung von Reentrantlock (Reentrantlock) implementiert.
private static lock lock = new Reentrantlock (); public static void add () {lock.lock (); i ++; lock.unlock (); } CAS implementiert Spin -Lock
Warum verwenden Sie CAS? Da das Verschließen oder Verwenden von synchronisierten Schlüsselwörtern einen großen Leistungsverlust bringt, kann die Verwendung von CAS optimistische Verriegelung erzielen. Es wird tatsächlich direkt Anweisungen auf CPU-Ebene verwendet, sodass die Leistung sehr hoch ist.
Wie oben erwähnt, ist CAS die Grundlage für die Implementierung von Spinschlössern. CAS verwendet CPU -Anweisungen, um die Atomizität des Betriebs zu gewährleisten, um den Lock -Effekt zu erreichen. Was Spin betrifft, ist es auch sehr klar, die wörtliche Bedeutung zu lesen. Wenn Sie es selbst drehen, ist es eine Schleife. Es wird im Allgemeinen mit einer unendlichen Schleife implementiert. Auf diese Weise wird eine CAS -Operation in einer unendlichen Schleife ausgeführt. Wenn die Operation erfolgreich ist und wahr zurückgibt, endet die Schleife. Wenn falsch ist, wird die Schleife ausgeführt und die CAS -Operation wird fortgesetzt, bis True zurückgegeben wird.
Tatsächlich verwenden viele Orte im JDK CAS, insbesondere im Java.util.Concurrent -Paket, wie Countdownlatch, Semaphore, Reentrantlock und Java.util.Concurrent.atomic Paket. Ich glaube, jeder hat Atomic*verwendet, wie Atomicboolean, Atomicinger usw.
Hier nehmen wir Atomicboolean als Beispiel, weil es einfach genug ist.
öffentliche Klasse atomicboolan implementiert java.io.serializable {private statische endgültige long serialversionuid = 4654671469794556979l; // Setup zur Verwendung von unafe.comPareAndswapint für Updates private statische endgültige unsichere unafe = unafe.getunsafe (); private statische endgültige Long ValueOffset; static {try {valueOffset = unafe.ObjectFieldOffset (atomicboolean.class.getDeclaredfield ("value")); } catch (Ausnahme ex) {neue Fehler werfen (ex); }} private volatile int -Wert; public final boolean get () {return value! = 0; } public Final Boolean Vergleiche (boolean erwarten, boolean update) {int e = erwarten? 1: 0; int u = update? 1: 0; return unafe.comPareAndswapint (this, ValueOffset, e, u); }}Dies ist Teil des Code von Atomicboolean, und wir sehen hier mehrere Schlüsselmethoden und Eigenschaften.
1. Das Sun.Misc.unsafe -Objekt wird verwendet. Diese Klasse bietet eine Reihe von Methoden, um Speicherobjekte direkt zu betreiben, wird jedoch nur intern von JDK verwendet und wird nicht für Entwickler empfohlen.
2. Wert repräsentiert den tatsächlichen Wert. Sie können sehen, dass die GET -Methode den booleschen Wert tatsächlich beurteilt, basierend darauf, ob der Wert gleich 0 ist. Der Wert hier ist als volatil definiert, da volatile die Sichtbarkeit der Speicher sicherstellen kann, dh, sofern sich der Wertwert ändert, können andere Threads den geänderten Wert sofort sehen. Der nächste Artikel wird über die Sichtbarkeit von volatilen Befolgen sprechen, willkommen zu folgen
3.. ValueOffSet ist der Speicherversatz des Wertwerts, der unter Verwendung der unsicheren Methode für die .ObjectFieldOffset erhalten und als nachfolgende Vergleichs -Methode verwendet wird.
4. VergleicheSet -Methode ist die Kernmethode zur Implementierung von CAS. Bei Verwendung der AtomicBoolean -Methode müssen Sie nur den erwarteten Wert und den zu aktualisierten Wert übergeben. Die methode unafe.comPareAndswapint (diese, ValueOffset, E, U) wird aufgerufen. Es handelt sich um eine native Methode, die in C ++ implementiert wird, und der spezifische Code wird nicht veröffentlicht. Kurz gesagt, es verwendet die CPU -CMMPXCHG -Anweisung der CPU, um den Vergleich und den Austausch zu vervollständigen. Abhängig von der spezifischen Systemversion gibt es natürlich auch Unterschiede in der Implementierung. Diejenigen, die interessiert sind, können selbst nach den relevanten Artikeln suchen.
Szenarien verwenden
Zum Beispiel kann Atomicboolean in einem solchen Szenario verwendet werden. Das System muss feststellen, ob einige Initialisierungsvorgänge basierend auf den staatlichen Eigenschaften einer booleschen Variablen durchgeführt werden müssen. Wenn es sich um eine Multi-Thread-Umgebung handelt und wiederholte Ausführungen vermeiden kann, kann sie mit Atomicboolean implementiert werden. Der Pseudocode lautet wie folgt:
private endgültige statische Atomicboolan Flag = New Atomicboolean (); if (flag.comPareandset (false, true)) {init (); }Zum Beispiel kann Atomicinder in Zählern und in mehreren Thread-Umgebungen verwendet werden, um eine genaue Zählung sicherzustellen.
ABA Fragen
Es gibt ein Problem mit CAS, nämlich dass sich ein Wert von A nach B und dann von B nach A ändert. In diesem Fall wird CAS glauben, dass sich der Wert nicht geändert hat, aber tatsächlich hat er sich geändert. In dieser Hinsicht gibt es AtomicStampedReference unter gleichzeitigen Paketen, die eine Implementierung basieren, die auf der Versionsnummer basiert und einige Probleme lösen kann.
Zusammenfassen
Das obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, dass der Inhalt dieses Artikels einen gewissen Referenzwert für das Studium oder die Arbeit eines jeden hat. Wenn Sie Fragen haben, können Sie eine Nachricht zur Kommunikation überlassen. Vielen Dank für Ihre Unterstützung bei Wulin.com.