Heim  >  Artikel  >  Java  >  So implementieren Sie eine verteilte Java-Sperre

So implementieren Sie eine verteilte Java-Sperre

王林
王林nach vorne
2023-04-25 18:19:071725Durchsuche

1. Einführung in verteilte Sperren

Einzelne Maschinen-Multithreading: In Java verwenden wir normalerweise lokale Sperren wie die ReetrantLock-Klasse und das synchronisierte Schlüsselwort, um den Zugriff mehrerer Threads in einem JVM-Prozess auf lokale gemeinsam genutzte Ressourcen zu steuern

So implementieren Sie eine verteilte Java-Sperre

Verteiltes System: Verschiedene Dienste/Clients laufen in der Regel auf unabhängigen JVM-Prozessen. Wenn mehrere JVM-Prozesse dieselbe Ressource gemeinsam nutzen, gibt es keine Möglichkeit, mithilfe lokaler Sperren einen sich gegenseitig ausschließenden Zugriff auf die Ressource zu erreichen. So wurden verteilte Schlösser geboren.

Zum Beispiel: Es werden insgesamt 3 Kopien des Bestelldienstes des Systems eingesetzt, die alle Dienste für die Außenwelt bereitstellen. Benutzer müssen den Lagerbestand überprüfen, bevor sie eine Bestellung aufgeben. Um einen Überverkauf zu verhindern, ist eine Sperre erforderlich, um einen synchronen Zugriff auf den Lagerbestandsprüfvorgang zu erreichen. Da sich der Bestellservice in einem anderen JVM-Prozess befindet, funktionieren lokale Sperren in diesem Fall nicht ordnungsgemäß. Wir müssen verteilte Sperren verwenden, damit mehrere Threads, auch wenn sie sich nicht im selben JVM-Prozess befinden, dieselbe Sperre erhalten und so einen sich gegenseitig ausschließenden Zugriff auf gemeinsam genutzte Ressourcen erreichen können.

So implementieren Sie eine verteilte Java-Sperre

Eine grundlegendste verteilte Sperre muss Folgendes erfüllen:

  • Gegenseitiger Ausschluss: Die Sperre kann jederzeit nur von einem Thread gehalten werden;

  • Hohe Verfügbarkeit: Der Sperrdienst ist hochverfügbar. Selbst wenn es ein Problem mit der Codelogik des Clients zum Aufheben der Sperre gibt, wird die Sperre schließlich aufgehoben und hat keinen Einfluss auf den Zugriff anderer Threads auf gemeinsam genutzte Ressourcen.

  • Reentrant: Nachdem ein Knoten die Sperre erworben hat, kann er die Sperre erneut erwerben.

2. Implementieren verteilter Sperren basierend auf Redis

1 So implementieren Sie die einfachste verteilte Sperre basierend auf Redis

Ob es sich um eine lokale Sperre oder eine verteilte Sperre handelt, der Kern liegt im „gegenseitigen Ausschluss“ ==.

In Redis, SETNX 命令是可以帮助我们实现互斥。SETNXDas heißt, SET if Not eXists (entsprechend der setIfAbsent-Methode in Java, wenn der Schlüssel nicht vorhanden ist, wird der Wert des Schlüssels festgelegt). Wenn der Schlüssel bereits vorhanden ist, führt SETNX keine Aktion aus.

> SETNX lockKey uniqueValue
(integer) 1
> SETNX lockKey uniqueValue

Um die Sperre aufzuheben, löschen Sie den entsprechenden Schlüssel direkt über den DEL-Befehl

> DEL lockKey

( Ganzzahl) 1

Um ein versehentliches Löschen anderer Sperren zu verhindern, empfehlen wir hier die Verwendung eines Lua-Skripts, um anhand des dem Schlüssel entsprechenden Werts (eindeutiger Wert) zu beurteilen.

Das Lua-Skript wurde ausgewählt, um die Atomizität des Entsperrvorgangs sicherzustellen. Denn Redis kann Lua-Skripte atomar ausführen und so die Atomizität des Sperrfreigabevorgangs sicherstellen.

// 释放锁时,先比较锁对应的 value 值是否相等,避免锁的误释放
if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end

So implementieren Sie eine verteilte Java-Sperre

Dies ist die einfachste Implementierung einer verteilten Redis-Sperre. Die Implementierungsmethode ist relativ einfach und die Leistung ist sehr effizient. Es gibt jedoch einige Probleme bei der Implementierung verteilter Sperren auf diese Weise. Wenn die Anwendung beispielsweise auf einige Probleme stößt, z. B. wenn die Logik zum Aufheben der Sperre plötzlich einfriert, wird die Sperre möglicherweise nicht aufgehoben und andere Threads/Prozesse können nicht mehr auf die gemeinsam genutzten Ressourcen zugreifen.

2. Warum eine Ablaufzeit für die Sperre festlegen? Hauptsächlich, um zu verhindern, dass die Sperre aufgehoben wird ;

uniqueValue: eine zufällige Zeichenfolge, die das Schloss eindeutig identifizieren kann;


NX: SET kann nur erfolgreich sein, wenn der dem lockKey entsprechende Schlüsselwert nicht vorhanden ist;
  • EX: Ablaufzeiteinstellung ( in Sekunden) ) EX 3 gibt an, dass diese Sperre eine automatische Ablaufzeit von 3 Sekunden hat. EX entspricht PX (in Millisekunden), beides sind Ablaufzeiteinstellungen.

  • Stellen Sie sicher, dass das Festlegen des Werts und der Ablaufzeit des angegebenen Schlüssels eine atomare Operation ist! ! ! Andernfalls besteht möglicherweise immer noch das Problem, dass die Sperre nicht aufgehoben werden kann.

  • Diese Lösung weist auch Lücken auf:
  • Wenn die Zeit zum Betreiben der gemeinsam genutzten Ressource länger als die Ablaufzeit ist, läuft die Sperre vorzeitig ab, was zum direkten Ausfall der verteilten Sperre führt.
  • Wenn die Sperrzeitlimit ist festgelegt. Wenn es zu lang ist, wirkt sich dies auf die Leistung aus. Einschließlich nicht nur mehrerer Distributionen. Implementierung der Sperre. Darüber hinaus unterstützt Redisson auch mehrere Bereitstellungsarchitekturen wie Redis Standalone, Redis Sentinel und Redis Cluster.

Die verteilte Sperre in Redisson verfügt über einen automatischen Erneuerungsmechanismus. Er ist sehr einfach zu verwenden und das Prinzip ist relativ einfach. Er bietet einen Watch Dog, der speziell zum Überwachen und Erneuern der Sperre verwendet wird, wenn der Thread vorhanden ist Wenn die Ausführung nicht abgeschlossen ist, verlängert Watch Dog kontinuierlich die Ablaufzeit der Sperre, um sicherzustellen, dass die Sperre aufgrund einer Zeitüberschreitung nicht aufgehoben wird.

So implementieren Sie eine verteilte Java-Sperre

使用方式举例:

// 1.获取指定的分布式锁对象
RLock lock = redisson.getLock("lock");
// 2.拿锁且不设置锁超时时间,具备 Watch Dog 自动续期机制
lock.lock();
// 3.执行业务
...
// 4.释放锁
lock.unlock();

只有未指定锁超时时间,才会使用到 Watch Dog 自动续期机制。

// 手动给锁设置过期时间,不具备 Watch Dog 自动续期机制
lock.lock(10, TimeUnit.SECONDS);

总的来说就是使用Redisson,它带有自动的续期机制

4. 如何实现可重入锁

所谓可重入锁指的是在一个线程中可以多次获取同一把锁,比如一个线程在执行一个带锁的方法,该方法中又调用了另一个需要相同锁的方法,则该线程可以直接执行调用的方法即可重入 ,而无需重新获得锁。像 Java 中的 synchronized 和 ReentrantLock 都属于可重入锁。

可重入分布式锁的实现核心思路是线程在获取锁的时候判断是否为自己的锁,如果是的话,就不用再重新获取了。为此,我们可以为每个锁关联一个可重入计数器和一个占有它的线程。当可重入计数器大于 0 时,则锁被占有,需要判断占有该锁的线程和请求获取锁的线程是否为同一个。

Das obige ist der detaillierte Inhalt vonSo implementieren Sie eine verteilte Java-Sperre. 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