Heim  >  Artikel  >  Java  >  So verwenden Sie die StampedLock-Sperre in der gleichzeitigen Java-Programmierung

So verwenden Sie die StampedLock-Sperre in der gleichzeitigen Java-Programmierung

WBOY
WBOYnach vorne
2023-04-24 08:01:061291Durchsuche

StampedLock:

StampedLock ist eine neue Sperre in der JDK8-Version des Concurrent-Pakets. Diese Sperre bietet drei Modi der Lese- und Schreibsteuerung beim Aufrufen einer Reihe von Funktionen, um die Sperre zu erhalten. Es wird eine lange Variable zurückgegeben, die als Stempel bezeichnet wird. Dieser Stempel repräsentiert den Status der Sperre. Die try-Reihe von Funktionen, die Sperren erwerben, gibt den Stempelwert 0 zurück, wenn die Sperrenerfassung fehlschlägt. Beim Aufruf der Methoden zum Freigeben von Sperren und zum Konvertieren von Sperren müssen Sie den beim Erwerb der Sperre zurückgegebenen Stempelwert übergeben. StampedLock是并发包里面JDK8版本新增的一个锁,该锁提供了三种模式的读写控制,当调用获取锁的系列函数时,会返回一个long 型的变量,我们称之为戳记(stamp),这个戳记代表了锁的状态。其中try 系列获取锁的函数,当获取锁失败后会返回为0的stamp值。当调用释放锁和转换锁的方法时需要传入获取锁时返回的stamp值。

StampedLock提供的三种读写模式的锁分别如下:

  • 写锁witeLock: 是一个排它锁或者独占锁,某时只有一个线程可以获取该锁,当一个线程获取该锁后,其他请求读锁和写锁的线程必须等待,这类似于 ReentrantReadWriteLock的写锁(不同的是这里的写锁是不可重入锁):当目前没有线程持有读锁或者写锁时才可以获取到该锁。请求该锁成功后会返回一个stamp变量用来表示该锁的版本,当释放该锁时需要调用unlockWrite方法并传递获取锁日的 stamp 参数。并且它提供了非阻塞的tryWriteLock 方法。

  • 悲观读锁 readLock: 是一个共享锁,在没有线程获取独占写锁的情况下,多个线程可以同时获取该锁。如果已经有线程持有写锁,则其他线程请求获取该读锁会被阻塞,这类似于 ReentrantReadWriteLock 的读锁(不同的是这里的读锁是不可重入锁)。这里说的悲观是指在具体操作数据前其会悲观地认为其他线程可能要对自己操作的数据进行修改,所以需要先对数据加锁,这是在读少写多的情况下的一种考虑。请求该锁成功后会返回一个stamp变量用来表示该锁的版本,当释放该锁时需要调用 unlockRead 方法并传递 stamp 参数。并且它提供了非阻塞的 tryReadLock 方法。

  • 乐观读锁tryOptimisticRead: 它是相对于悲观锁来说的,在操作数据前并没有通过 CAS设置锁的状态,仅仅通过位运算测试。如果当前没有线程持有写锁,则简单地返回一个非0的stamp版本信息。获取该 stamp 后在具体操作数据前还需要调用 validate 方法验证该 stamp 是否已经不可用,也就是看当调用 tryOptimisticRead 返回 stamp后到当前时间期间是否有其他线程持有了写锁,如果是则validate 会返回0否则就可以使用该stamp版本的锁对数据进行操作。由于tryOptimisticRead 并没有使用CAS设置锁状态,所以不需要显式地释放该锁。该锁的一个特点是适用于读多写少的场景,因为获取读锁只是使用位操作进行检验,不涉及CAS操作,所以效率会高很多,但是同时由于没有使用真正的锁,在保证数据一致性上需要复制一份要操作的变量到方法栈,并且在操作数据时可能其他写线程已经修改了数据,而我们操作的是方法栈里面的数据,也就是一个快照,所以最多返回的不是最新的数据,但是一致性还是得到保障的。

StampedLock还支持这三种锁一定条件下进行相互转换。例如long tryConvertToWriteLock(long stamp)期望把stamp 标示的锁升级为写锁,

这个函数会在下面几种情况下返回一个有效的stamp(也就是晋升写锁成功):

  • 当前锁已经是写锁模式了。

  • 前锁处于读锁模式,并且没有其他线程是读锁模式

  • 当前处于乐观读模式,并且当前写锁可用

另外,StampedLock

Die drei von StampedLock bereitgestellten Lese- und Schreibmodussperren lauten wie folgt:

  • 🎜Schreibsperre witeLock: 🎜 ist eine exklusive Sperre oder exklusive Sperre, a bestimmte Nur ein Thread kann die Sperre erwerben, andere Threads, die Lesesperren und Schreibsperren anfordern, müssen warten. Dies ähnelt der Schreibsperre von ReentrantReadWriteLock (der Unterschied besteht darin, dass die Schreibsperre hier keine Wiedereintrittssperre ist ) :Die Sperre kann nur erworben werden, wenn derzeit kein Thread eine Lese- oder Schreibsperre besitzt. Nach erfolgreicher Anforderung der Sperre wird eine Stempelvariable zurückgegeben, die die Version der Sperre darstellt. Wenn die Sperre aufgehoben wird, muss die Methode unlockWrite aufgerufen und der Stempelparameter des Sperrdatums übergeben werden. Und es bietet eine nicht blockierende tryWriteLock-Methode. 🎜
  • 🎜🎜Pessimistische Lesesperre readLock: 🎜 ist eine gemeinsame Sperre. Wenn kein Thread eine exklusive Schreibsperre erhält, können mehrere Threads gleichzeitig die Sperre erwerben. Wenn ein Thread bereits über eine Schreibsperre verfügt, werden die Anforderungen anderer Threads zum Erwerb der Lesesperre blockiert. Dies ähnelt der Lesesperre von ReentrantReadWriteLock (der Unterschied besteht darin, dass die Lesesperre hier eine nicht wiedereintrittsfähige Sperre ist). Der hier erwähnte Pessimismus bedeutet, dass er pessimistisch davon ausgeht, dass andere Threads die Daten, die er bearbeitet, ändern könnten, bevor er die Daten speziell verarbeitet. Daher muss er zuerst die Daten sperren. Dies ist eine Überlegung, wenn weniger gelesen und mehr geschrieben wird. Nach erfolgreicher Anforderung der Sperre wird eine Stempelvariable zurückgegeben, die die Version der Sperre darstellt. Wenn die Sperre aufgehoben wird, muss die Methode unlockRead aufgerufen und der Stempelparameter übergeben werden. Und es bietet eine nicht blockierende tryReadLock-Methode. 🎜
  • 🎜🎜Optimistische Lesesperre tryOptimisticRead: 🎜Es ist relativ zur pessimistischen Sperre. Der Status der Sperre wird vor dem Betrieb von Daten nicht durch CAS festgelegt, sondern nur durch Bitoperationen getestet. Wenn derzeit kein Thread die Schreibsperre hält, wird einfach eine Stempelversionsinformation ungleich Null zurückgegeben. Nachdem Sie den Stempel erhalten haben, müssen Sie die Validierungsmethode aufrufen, um zu überprüfen, ob der Stempel nicht mehr verfügbar ist, bevor Sie die Daten gezielt verarbeiten. Das heißt, ob andere Threads zwischen dem Zeitpunkt, zu dem tryOptimisticRead den Stempel zurückgibt, und dem aktuellen die Schreibsperre halten Wenn ja, gibt „validate“ 0 zurück, andernfalls können Sie die Stempelversion der Sperre verwenden, um die Daten zu bearbeiten. Da tryOptimisticRead CAS nicht zum Festlegen des Sperrstatus verwendet, besteht keine Notwendigkeit, die Sperre explizit aufzuheben. Ein Merkmal dieser Sperre ist, dass sie für Szenarien geeignet ist, in denen es viele Lesevorgänge und wenige Schreibvorgänge gibt. Da beim Erwerb der Lesesperre nur Bitoperationen zur Überprüfung verwendet werden und keine CAS-Operationen erforderlich sind, ist die Effizienz jedoch viel höher Da keine echte Sperre verwendet wird, ist gleichzeitig die Datenkonsistenz gewährleistet. Es ist notwendig, die zu bedienenden Variablen in den Methodenstapel zu kopieren. Beim Bearbeiten der Daten haben möglicherweise andere Schreibthreads die Daten geändert, und wir führen sie aus Daten im Methodenstapel, bei denen es sich um eine Momentaufnahme handelt, sodass die am häufigsten zurückgegebenen Daten nicht die neuesten sind, die Konsistenz jedoch weiterhin gewährleistet ist. 🎜
🎜StampedLock unterstützt unter bestimmten Bedingungen auch die gegenseitige Umwandlung dieser drei Schlösser. Beispielsweise erwartet long tryConvertToWriteLock (langer Stempel), dass die durch den Stempel gekennzeichnete Sperre zu einer Schreibsperre aktualisiert wird. 🎜🎜🎜Diese Funktion gibt in den folgenden Situationen einen gültigen Stempel zurück (d. h. die Schreibsperre). Promotion ist erfolgreich) :🎜🎜
  • 🎜Die aktuelle Sperre befindet sich bereits im Schreibsperrmodus. 🎜
  • 🎜Die vorherige Sperre befindet sich im Lesesperrmodus und kein anderer Thread befindet sich im Lesesperrmodus.🎜
  • 🎜Es befindet sich derzeit im optimistischen Lesemodus und die aktuelle Schreibsperre ist verfügbar🎜
  • 🎜Darüber hinaus sind die Lese- und Schreibsperren von StampedLock nicht wiedereintrittsfähige Sperren, sodass Sie nach dem Erwerb der Sperre und vor der Freigabe der Sperre keine Vorgänge aufrufen sollten Dadurch wird die Sperre erworben, um zu verhindern, dass der aufrufende Thread blockiert wird. Wenn mehrere Threads gleichzeitig versuchen, Lesesperren und Schreibsperren zu erwerben, gibt es keine bestimmten Regeln dafür, wer die Sperre zuerst erhält. Dies erfolgt vollständig nach dem Best-Effort-Prinzip und erfolgt zufällig. Und die Sperre implementiert die Lock- oder ReadWriteLock-Schnittstelle nicht direkt, sondern verwaltet intern eine bidirektionale Blockierungswarteschlange. 🎜🎜Das Folgende ist ein Beispiel für die Verwaltung zweidimensionaler Punkte, die in JDK8 bereitgestellt werden, um die oben eingeführten Konzepte zu verstehen. 🎜
    package LockSupportTest;
    
    import com.sun.org.apache.bcel.internal.generic.BREAKPOINT;
    
    import java.util.concurrent.locks.StampedLock;
    
    public class Point_Class {
        private double x,y;
        private final StampedLock sl = new StampedLock();
        
        void move(double deltaX, double deltaY) {
            long stamp = sl.writeLock();
            try {
                x += deltaX;
                y += deltaY;
            } finally {
                sl.unlockWrite(stamp);
            }
        }
        
        double distanceFromOrin() {
            long stamp = sl.tryOptimisticRead();
            double currentX = x, currentY = y;
            if (!sl.validate(stamp)) {
                stamp = sl.readLock();
                try {
                    currentX = x;
                    currentY = y;
                } finally {
                    sl.unlockRead(stamp);
                }
            }
            return Math.sqrt(currentX*currentX + currentY*currentY);
        }
        
        void moveIfAtOrigin(double newX, double newY) {
            long stamp = sl.readLock();
            try {
                while (x == 0.0 && y == 0.0) {
                    long ws = sl.tryConvertToWriteLock(stamp);
                    if (ws != 0L) {
                        stamp = ws;
                        x = newX;
                        y = newY;
                        break;
                    } else {
                        sl.unlockRead(stamp);
                        stamp = sl.writeLock();
                    }
                }
            } finally {
                        sl.unlock(stamp);
                    }
                }
    }
    🎜Im obigen Code verfügt die Point-Klasse über zwei Mitgliedsvariablen (x, y), die zur Darstellung der zweidimensionalen Koordinaten eines Punkts verwendet werden, und drei Methoden zum Betreiben von Koordinatenvariablen. Darüber hinaus wird ein StampedLock-Objekt instanziiert, um die Atomizität der Operation sicherzustellen. 🎜

Das obige ist der detaillierte Inhalt vonSo verwenden Sie die StampedLock-Sperre in der gleichzeitigen Java-Programmierung. 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