Maison  >  Article  >  Java  >  Comment utiliser le verrou StampedLock dans la programmation simultanée Java

Comment utiliser le verrou StampedLock dans la programmation simultanée Java

WBOY
WBOYavant
2023-04-24 08:01:061288parcourir

StampedLock :

StampedLock est un nouveau verrou dans la version JDK8 du package concurrent. Ce verrou fournit trois modes de contrôle de lecture et d'écriture. Lors de l'appel d'une série de fonctions pour acquérir le verrou, il renverra Une variable longue est appelée un tampon. Ce tampon représente l'état du verrou. La série try de fonctions qui acquièrent des verrous renverra une valeur de tampon de 0 lorsque l'acquisition du verrou échoue. Lorsque vous appelez les méthodes de libération des verrous et de conversion des verrous, vous devez transmettre la valeur du tampon renvoyée lors de l'acquisition du verrou. 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

Les trois verrous en mode lecture et écriture fournis par StampedLock sont les suivants :

  • 🎜verrouillage en écriture witeLock : 🎜 est un verrou exclusif ou un verrou exclusif, un certain Un seul thread peut acquérir le verrou. Lorsqu'un thread acquiert le verrou, les autres threads demandant des verrous en lecture et en écriture doivent attendre. Ceci est similaire au verrou en écriture de ReentrantReadWriteLock (la différence est que le verrou en écriture ici n'est pas un verrou réentrant). ) : Le verrou ne peut être acquis que lorsqu'aucun thread ne détient actuellement de verrou en lecture ou en écriture. Après avoir demandé avec succès le verrou, une variable stamp sera renvoyée pour représenter la version du verrou. Lorsque le verrou est libéré, la méthode unlockWrite doit être appelée et le paramètre stamp de la date du verrou doit être transmis. Et il fournit une méthode tryWriteLock non bloquante. 🎜
  • 🎜🎜Verrou de lecture pessimiste readLock : 🎜 est un verrou partagé Lorsqu'aucun thread n'acquiert un verrou en écriture exclusif, plusieurs threads peuvent acquérir le verrou en même temps. Si un thread détient déjà le verrou en écriture, les demandes des autres threads pour acquérir le verrou en lecture seront bloquées, ce qui est similaire au verrou en lecture de ReentrantReadWriteLock (la différence est que le verrou en lecture ici n'est pas un verrou réentrant). Le pessimisme mentionné ici signifie qu'avant d'exploiter spécifiquement les données, il croira avec pessimisme que d'autres threads peuvent modifier les données qu'il exploite, il doit donc d'abord verrouiller les données. C'est une considération lors de la lecture moins et de l'écriture davantage. Après avoir demandé avec succès le verrou, une variable stamp sera renvoyée pour représenter la version du verrou. Lorsque le verrou est libéré, la méthode unlockRead doit être appelée et le paramètre stamp transmis. Et il fournit une méthode tryReadLock non bloquante. 🎜
  • 🎜🎜Verrouillage de lecture optimiste tryOptimisticRead : 🎜Il est relatif au verrou pessimiste. L'état du verrou n'est pas défini via CAS avant l'exploitation des données, mais n'est testé que via des opérations sur bits. Si aucun thread ne détient actuellement le verrou en écriture, une information de version non nulle est simplement renvoyée. Après avoir obtenu le tampon, vous devez appeler la méthode validate pour vérifier si le tampon n'est plus disponible avant d'exploiter spécifiquement les données, c'est-à-dire s'il existe d'autres threads détenant le verrou en écriture entre le moment où tryOptimisticRead renvoie le tampon et le courant. Si tel est le cas, la validation renverra 0, sinon vous pourrez utiliser la version tampon du verrou pour opérer sur les données. Étant donné que tryOptimisticRead n'utilise pas CAS pour définir l'état du verrou, il n'est pas nécessaire de libérer explicitement le verrou. Une caractéristique de ce verrou est qu'il convient aux scénarios dans lesquels il y a beaucoup de lectures et peu d'écritures. Étant donné que l'acquisition du verrou de lecture utilise uniquement des opérations sur les bits pour la vérification et n'implique pas d'opérations CAS, l'efficacité sera cependant beaucoup plus élevée. En même temps, puisqu'aucun verrou réel n'est utilisé, la cohérence des données est assurée. Il est nécessaire de copier les variables à exploiter dans la pile de méthodes, et lors de l'exploitation des données, d'autres threads d'écriture peuvent avoir modifié les données, et ce que nous opérons est. les données de la pile de méthodes, qui sont un instantané, donc les données les plus renvoyées ne sont pas les dernières données, mais la cohérence est toujours garantie. 🎜
🎜StampedLock prend également en charge la conversion mutuelle de ces trois verrous sous certaines conditions. Par exemple, long tryConvertToWriteLock (long stamp) s'attend à mettre à niveau le verrou marqué par stamp vers un verrou en écriture 🎜🎜🎜Cette fonction renverra un tampon valide dans les situations suivantes (c'est-à-dire le verrou en écriture). la promotion est réussie) :🎜🎜
  • 🎜Le verrou actuel est déjà en mode verrouillage en écriture. 🎜
  • 🎜Le verrou précédent est en mode de verrouillage en lecture et aucun autre thread n'est en mode de verrouillage en lecture🎜
  • 🎜Il est actuellement en mode de lecture optimiste et le verrou d'écriture actuel est disponible🎜
  • 🎜De plus, les verrous en lecture et en écriture de StampedLock sont des verrous non réentrants, donc après avoir acquis le verrou et avant de le libérer, vous ne devez pas appeler d'opérations qui obtiendra le verrou pour éviter de bloquer le thread appelant. Lorsque plusieurs threads tentent d'acquérir des verrous de lecture et d'écriture en même temps, il n'existe pas de règles précises pour déterminer qui acquiert le verrou en premier. Cela se fait au mieux et de manière aléatoire. Et le verrou n'implémente pas directement l'interface Lock ou ReadWriteLock, mais maintient une file d'attente de blocage bidirectionnelle en interne. 🎜🎜Ce qui suit est un exemple de gestion de points bidimensionnels fournis dans le JDK8 pour comprendre les concepts introduits ci-dessus. 🎜
    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);
                    }
                }
    }
    🎜Dans le code ci-dessus, la classe Point a deux variables membres (x, y) utilisées pour représenter les coordonnées bidimensionnelles d'un point et trois méthodes pour faire fonctionner les variables de coordonnées. De plus, un objet StampedLock est instancié pour garantir l’atomicité de l’opération. 🎜

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer