Verteilte Sperren können auf viele Arten implementiert werden, z. B. als Zookeeper, Redis ... In beiden Fällen bleibt das Grundprinzip dasselbe: Zur Darstellung der Sperre wird ein Zustandswert verwendet, und die Belegung und Freigabe der Sperre wird durch den Zustandswert identifiziert.
1. Warum Redis problemlos verteilte Sperren implementieren kann
1 queues Der Modus verwandelt den gleichzeitigen Zugriff in einen seriellen Zugriff, und es gibt keine Konkurrenz zwischen den Verbindungen mehrerer Clients zu Redis.
2. Der SETNX-Befehl von Redis kann problemlos verteilte Sperren implementieren.
setNX (SET if Not eXists)
Syntax: SETNX-Schlüsselwert
Rückgabewert: Wenn die Einstellung erfolgreich ist, wird 1 zurückgegeben; wenn die Einstellung fehlschlägt, ist 0 zurückgegeben.
Setzen Sie den Wert des Schlüssels genau dann auf Wert, wenn der Schlüssel nicht existiert, und geben Sie 1 zurück, wenn der angegebene Schlüssel bereits existiert. SETNX führt keine Aktion aus und gibt 0 zurück.
Zusammenfassend lässt sich sagen, dass Sie den Rückgabewert von setnx verwenden können, um zu bestimmen, ob die Sperre erworben wurde, und Sie müssen sich keine Gedanken über den gleichzeitigen Zugriff machen, da Redis Single-Threaded ist, also wenn es 1 zurückgibt , die Sperre wird erworben und 0 wird zurückgegeben. Sie wurde nicht erhalten. Wenn der Geschäftsvorgang abgeschlossen ist, muss die Sperre aufgehoben werden. Die Logik zum Aufheben der Sperre besteht darin, den zuvor festgelegten Schlüssel zu löschen, damit die Sperre beim nächsten Mal durch Festlegen des Schlüssels abgerufen werden kann.
2. Implementierung verteilter Sperren
Wir wissen bereits, dass verteilte Sperren über die Redis-eigene Funktion setNX implementiert werden können. Die spezifischen Implementierungsschritte sind wie folgt.
Ich habe den Redis-Dienst in einer virtuellen CentOS7-Linux-Maschine installiert. Die IP-Adresse lautet: 192.168.246.130 und der Dienst-Port ist: 6379.
Das Folgende ist ein Beispiel dafür, wie Java verteilte Sperren über Redis implementiert:
import redis.clients.jedis.Jedis; public class RedisLock { //锁的key private static final String key = "DistributedRedisLock"; private static Integer count = 0; public static void main(String[] args) { for(int i=0;i<1000;i++){ new Thread(new Runnable() { @Override public void run() { //获取Redis连接 Jedis jedis = new Jedis("192.168.246.130", 6379); try{ while(true){ //获取锁 if(jedis.setnx(key, Thread.currentThread().getName()) == 1){ try{ System.out.println("线程("+Thread.currentThread().getName()+")获取到锁,开始执行操作"); count++; System.out.println(count); break; }finally{ System.out.println("操作执行完成,释放锁"); //操作执行完一定要释放锁,所以在finally块中执行 jedis.del(key); } }else{ //返回的不是1,说明已经有某个线程获取到了锁 try { //等待100毫秒之后重试 Thread.sleep(100l); } catch (InterruptedException e) { e.printStackTrace(); } } } }catch(Exception e){ e.printStackTrace(); }finally{ //释放Redis连接 jedis.disconnect(); } } }).start(); } } }
Die Ausgabe des obigen Codes lautet:
线程(Thread-320)获取到锁,开始执行操作 1 操作执行完成,释放锁 线程(Thread-463)获取到锁,开始执行操作 2 操作执行完成,释放锁 线程(Thread-997)获取到锁,开始执行操作 3 操作执行完成,释放锁 ... 线程(Thread-409)获取到锁,开始执行操作 998 操作执行完成,释放锁 线程(Thread-742)获取到锁,开始执行操作 999 操作执行完成,释放锁 线程(Thread-286)获取到锁,开始执行操作 1000 操作执行完成,释放锁
Obwohl sich der obige Code in einer einzelnen Anwendung befindet mit mehreren Threads Es wurde getestet, aber selbst wenn mehrere Anwendungen und mehrere Threads verwendet werden, um Sperren in einer verteilten Umgebung zu erhalten, sind die Ergebnisse immer noch korrekt.
3. Lösen Sie das Deadlock-Problem
Der vorherige Beispielcode ist nur ein Testcode, nur um das Prinzip zu veranschaulichen. Das Beispiel selbst ist sehr einfach einige unüberlegte Aspekte. Wenn beispielsweise nach dem Erwerb der Sperre während der Ausführung des Geschäftsvorgangs ein Umgebungsproblem auftritt und die Verbindung zu Redis getrennt wird, kann die Sperre im endgültigen Block nicht aufgehoben werden, was dazu führt, dass andere Threads, die auf den Erwerb der Sperre warten, auf unbestimmte Zeit warten. Das ist es, was passiert.
Lösung:
Sie können in Redis eine Ablaufzeit für die Sperre festlegen, sodass die Sperre auch dann automatisch aufgehoben werden kann, wenn sie nicht aufgehoben werden kann Zeitspanne.
In Bezug auf den Code müssen Sie nach dem Erwerb der Sperre nur den folgenden Code zum Try-Anweisungsblock hinzufügen:
jedis.expire(key, 10); //这里给锁设置10秒的过期时间
Eine bessere Lösung:
Die Lösung in Kapitel 1 ist nicht sehr gut, da die Sperre automatisch aufgehoben wird, wenn die Verarbeitungszeit des Geschäftsvorgangs sehr lang ist und die festgelegte Ablaufzeit überschreitet Wird von anderen Benutzern verwendet, werden Sperren, die von anderen Threads gehalten werden, freigegeben, was zu Parallelitätsproblemen führt. Daher ist es sinnvoller, beim Aufheben der Sperre festzustellen, ob die Sperre abgelaufen ist. Wenn sie abgelaufen ist, besteht keine Notwendigkeit, sie erneut aufzuheben.
Ändern Sie im Code den Vorgang nach dem Erwerb der Sperre in den folgenden Code:
long start = System.currentTimeMillis(); //获取起始时间毫秒数 try{ jedis.expire(key, 10); ... }finally{ ... if(System.currentTimeMillis() < start+10*1000){ //如果之前设置的锁还未过期,则释放掉 jedis.del(key); } }
Das obige ist der detaillierte Inhalt vonJava implementiert eine verteilte Sperre basierend auf Redis. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!