Heim  >  Artikel  >  Java  >  So verwenden Sie CAS und Java Optimistic Locking

So verwenden Sie CAS und Java Optimistic Locking

王林
王林nach vorne
2023-05-01 20:07:161046Durchsuche

Was ist CAS

CAS ist CompareAndSwap, was Vergleich und Austausch bedeutet. Warum verwendet CAS keine Sperren, gewährleistet aber dennoch eine sichere Datenmanipulation unter gleichzeitigen Bedingungen? Der Name zeigt das Prinzip von CAS tatsächlich sehr intuitiv. Der spezifische Prozess der Datenänderung ist wie folgt: #🎜🎜 #Wenn Sie CAS zum Bearbeiten von Daten verwenden, übergeben Sie den ursprünglichen Wert der Daten und den zu ändernden Wert an die Methode

  1. Vergleichen Sie, ob der aktuelle Zielvariablenwert vorliegt ist derselbe wie der ursprüngliche Wert, der in übergeben wurde

  2. Wenn sie gleich sind, bedeutet dies, dass die Zielvariable nicht von anderen Threads geändert wurde. Ändern Sie einfach die Zielvariable Wert direkt

  3. #🎜🎜 #Wenn sich der Zielvariablenwert vom ursprünglichen Wert unterscheidet, beweist dies, dass die Zielvariable von anderen Threads geändert wurde und diese CAS-Änderung fehlgeschlagen ist #🎜 🎜#

  4. Aus dem obigen Prozess können wir sehen, dass CAS tatsächlich garantiert ist, dass die Daten sicher geändert werden können, es besteht jedoch die Möglichkeit eines Fehlers bei der Änderung , die Änderung der Zielvariablendaten ist nicht erfolgreich. Zu diesem Zeitpunkt müssen wir eine Schleife durchlaufen, um das Ergebnis der CAS-Änderung der Daten zu ermitteln, und es erneut versuchen, wenn dies fehlschlägt.
  5. Studenten, die genauer nachdenken, befürchten möglicherweise, dass der Vergleichs- und Ersetzungsvorgang von CAS selbst Probleme mit der Parallelitätssicherheit verursacht. In tatsächlichen Anwendungen werden Vergleiche und Ersetzungen nicht durchgeführt JDK stellt mit Hilfe der Hardware-Ebene sicher, dass Vergleich und Substitution eine atomare Aktion sind.

CAS implementiert sperrenfreie Programmierung

Sperrenfreie Programmierung bezieht sich auf den sicheren Betrieb gemeinsamer Variablen ohne Verwendung von Sperren#🎜 🎜#In Bei der gleichzeitigen Programmierung verwenden wir verschiedene Sperren, um die Sicherheit gemeinsam genutzter Variablen zu gewährleisten. Das heißt, es ist garantiert, dass andere Threads nicht dieselbe gemeinsam genutzte Variable bearbeiten können, wenn ein Thread die Bedienung der gemeinsam genutzten Variablen noch nicht abgeschlossen hat.

Die korrekte Verwendung von Sperren kann die Datensicherheit bei Parallelität gewährleisten. Wenn jedoch der Grad der Parallelität nicht hoch ist und der Wettbewerb nicht hart ist, wird der Erwerb und die Freigabe von Sperren zu unnötiger Leistungsverschwendung. In diesem Fall können Sie die Verwendung von CAS in Betracht ziehen, um die Datensicherheit zu gewährleisten und eine sperrenfreie Programmierung zu erreichen. Der obige CAS-Vorgang weist jedoch immer noch Mängel auf. Angenommen, der Wert der gemeinsam genutzten Variablen, auf die der aktuelle Thread zugreift, ist A. Während Thread 1 auf die gemeinsam genutzte Variable zugreift, bedient Thread 2 die gemeinsam genutzte Variable und weist ihr den Wert B zu. Nachdem Thread 2 seine eigene Logik verarbeitet hat, wird er verwendet weist der gemeinsam genutzten Variablen den Wert A zu. Zu diesem Zeitpunkt vergleicht Thread 1 den Wert A der gemeinsam genutzten Variablen mit dem ursprünglichen Wert A, geht fälschlicherweise davon aus, dass kein anderer Thread die gemeinsam genutzte Variable betreibt, und gibt direkt den Erfolg der Operation zurück. Das ist das ABA-Problem. Obwohl sich die meisten Unternehmen nicht darum kümmern müssen, ob andere Änderungen an gemeinsam genutzten Variablen vorgenommen wurden, kann das korrekte Ergebnis erzielt werden, solange der ursprüngliche Wert mit dem aktuellen Wert übereinstimmt. Es gibt jedoch einige sensible Szenarien, in denen nicht nur die Das Ergebnis der gemeinsam genutzten Variablen ist gleichbedeutend damit, dass sie nicht geändert wird, aber es ist auch nicht akzeptabel, dass gemeinsam genutzte Variablen von anderen Threads im Prozess geändert werden. Glücklicherweise gibt es eine ausgereifte Lösung für das ABA-Problem. Wir fügen der gemeinsam genutzten Variablen eine Versionsnummer hinzu, und der Wert der Versionsnummer erhöht sich automatisch, wenn die gemeinsam genutzte Variable geändert wird. Bei der CAS-Operation vergleichen wir nicht den ursprünglichen Variablenwert, sondern die Versionsnummer der gemeinsam genutzten Variablen. Die für jede Operation mit gemeinsam genutzten Variablen aktualisierte Versionsnummer ist eindeutig, sodass das ABA-Problem vermieden werden kann.

Spezifische Anwendungsszenarien

CAS-Anwendung im JDKErstens ist es unsicher, wenn mehrere Threads gleichzeitig mit gewöhnlichen Variablen arbeiten Das Operationsergebnis kann von anderen Threads überschrieben werden. Jetzt verwenden wir zwei Threads, und jeder Thread erhöht den Anfangswert 1. Wenn kein Synchronisationsmechanismus vorhanden ist, ist das Ergebnis der gemeinsam genutzten Variablen wahrscheinlich kleiner als 3 sein. Das heißt, es ist möglich, dass sowohl Thread 1 als auch Thread 2 den Anfangswert 1 gelesen haben, Thread 1 ihn 2 zuweist und der von Thread 2 aus dem Speicher gelesene Wert, in dem er sich befindet, auch die Variable erhöht durch 1 und weist es 2 zu, sodass das Endergebnis 2 ist, was kleiner ist als das erwartete Ergebnis von 3. Die Inkrementierungsoperation ist keine atomare Operation, was zu dem unsicheren Problem der gemeinsam genutzten Variablenoperation führt. Um dieses Problem zu lösen, stellt JDK eine Reihe atomarer Klassen bereit, um entsprechende atomare Operationen bereitzustellen. Das Folgende ist der Quellcode der getAndIncrement-Methode in AtomicInteger. Schauen wir uns den Quellcode an, um zu sehen, wie man mit CAS die threadsichere atomare Addition von Ganzzahlvariablen implementiert.

<code>/**<br> * 原子性的将当前值增加1<br> *<br> * @return 返回自增前的值<br> */<br>public final int getAndIncrement() {<br>    return unsafe.getAndAddInt(this, valueOffset, 1);<br>}<br></code>
Sie können sehen, dass getAndIncrement tatsächlich die getAndAddInt-Methode der UnSafe-Klasse aufruft, um atomare Operationen zu implementieren. Das Folgende ist der getAndAddInt-Quellcode

<code>/**<br> * 原子的将给定值与目标字变量相加并重新赋值给目标变量<br> *<br> * @param o 要更新的变量所在的对象<br> * @param offset 变量字段的内存偏移值<br> * @param delta 要增加的数字值<br> * @return 更改前的原始值<br> * @since 1.8<br> */<br>public final int getAndAddInt(Object o, long offset, int delta) {<br>    int v;<br>    do {<br>    	// 获取当前目标目标变量值<br>        v = getIntVolatile(o, offset);<br>    // 这句代码是关键, 自旋保证相加操作一定成功<br>    // 如果不成功继续运行上一句代码, 获取被其他<br>    // 线程抢先修改的变量值, 在新值基础上尝试相加<br>    // 操作, 保证了相加操作的原子性<br>    } while (!compareAndSwapInt(o, offset, v, v + delta));<br>    return v;<br>}<br></code>

Wir sind alle mit Sperren vertraut, z. B. mit wiedereintrittsfähigen Sperren, die im Wesentlichen auf der AbstractQueuedSynchronizer-Klasse basieren Warteschlange. Die Atomizität von Operationen wird durch CAS garantiert. Der Quellcode lautet wie folgt: Uusafe-Klasse im JDK Zusätzlich zu Operationen können wir CAS-Ideen in der tatsächlichen Entwicklung verwenden, um einen sicheren Datenbankbetrieb unter gleichzeitigen Bedingungen sicherzustellen.

Angenommen, die Struktur und Daten der Benutzertabelle sind wie folgt. Das Versionsfeld ist der Schlüssel zur Implementierung einer optimistischen Sperre

id

user

coupon_num

version

# 🎜 🎜#1#🎜 🎜#朱小明00

假设我们有一个用户领取优惠券的按钮,怎么防止用户快速点击按钮造成重复领取优惠券的情况呢。我们要安全的更改id为1的用户的coupon_num优惠券数量,将version字段作为CAS比较的版本号,即可避免重复增加优惠券数量,比较和替换这个逻辑通过WHERE条件来实现. 涉及sql如下:

<code>UPDATE user <br>SET coupon_num = coupon_num + 1, version = version + 1 <br>WHERE version = 0</code>

可以看到,我们查询出id为1的数据, 版本号为0,修改数据的同时把当前版本号当做条件即可实现安全修改,如果修改失败,证明已经被其他线程修改过,然后看具体业务决定是否需要自旋尝试再次修改。这里要注意考虑竞争激烈的情况下多个线程自旋导致过度的性能消耗,根据并发量选择适合自己业务的方式

Das obige ist der detaillierte Inhalt vonSo verwenden Sie CAS und Java Optimistic Locking. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen