En tant qu'outil permettant de partager simultanément des données et d'assurer la cohérence, les verrous ont plusieurs implémentations sur la plate-forme JAVA (telles que synchronisées et ReentrantLock, etc.). Ces verrous déjà écrits facilitent notre développement, mais la nature et le type spécifiques des verrous sont rarement mentionnés. Cette série d'articles analysera les noms et caractéristiques courants des verrous sous JAVA pour répondre à vos questions.
1. Verrouillage de la rotation
Les verrous de rotation sont implémentés en permettant au thread actuel de s'exécuter en continu dans le corps de la boucle. Ce n'est que lorsque les conditions de la boucle sont modifiées par d'autres threads que la section critique peut être saisie. Copiez le code comme suit :
classe publique SpinLock {
private AtomicReference<Thread> sign =new AtomicReference<>();
verrou public vide(){
Courant du fil = Thread.currentThread();
while(!sign .compareAndSet(null, current)){
}
}
déverrouillage du vide public () {
Courant du fil = Thread.currentThread();
signe .compareAndSet (actuel, nul);
}
}
À l'aide des opérations atomiques CAS, la fonction de verrouillage définit le propriétaire sur le thread actuel et prédit que la valeur d'origine est vide. La fonction de déverrouillage définit le propriétaire sur null et la valeur prédite est le thread actuel.
Lorsqu'un deuxième thread appelle l'opération de verrouillage, car la valeur du propriétaire n'est pas vide, la boucle est exécutée jusqu'à ce que le premier thread appelle la fonction de déverrouillage pour définir le propriétaire sur null, et le deuxième thread peut entrer dans la section critique.
Étant donné que le verrouillage de rotation permet uniquement au thread actuel d'exécuter le corps de la boucle sans modifier l'état du thread, la vitesse de réponse est plus rapide. Mais lorsque le nombre de threads continue d'augmenter, les performances diminuent considérablement car chaque thread doit être exécuté et prend du temps CPU. Si la concurrence entre les threads n'est pas intense et que le verrou est maintenu pendant un certain temps. Convient pour une utilisation avec des verrous rotatifs.
Remarque : Cet exemple est un verrou injuste. L'ordre d'obtention du verrou ne sera pas basé sur l'ordre d'entrée dans le verrou.
2. Autres types de verrous rotatifs
Nous avons parlé des verrous tournants ci-dessus. Il existe trois formes de verrouillage courantes dans les verrous tournants : TicketLock, CLHlock et MCSlock.
Le verrouillage des tickets résout principalement le problème de la séquence d'accès. Le problème principal concerne les processeurs multicœurs :
Copiez le code comme suit :
paquet com.alipay.titan.dcc.dal.entity ;
importer java.util.concurrent.atomic.AtomicInteger ;
classe publique TicketLock {
private AtomicInteger serviceNum = new AtomicInteger();
private AtomicInteger ticketNum = new AtomicInteger();
private static final ThreadLocal<Integer> LOCAL = new ThreadLocal<Integer>();
verrouillage public vide() {
int monticket = ticketNum.getAndIncrement();
LOCAL.set(monticket);
while (monticket != serviceNum.get()) {
}
}
public void unlock() {
int monticket = LOCAL.get();
serviceNum.compareAndSet(monticket, monticket + 1);
}
}
Un numéro de service serviceNum doit être interrogé à chaque fois, ce qui affecte les performances (il doit être lu dans la mémoire principale et il faut empêcher les autres CPU de le modifier).
CLHLock et MCSLock sont deux types similaires de verrous équitables, triés sous la forme d'une liste chaînée.
Copiez le code comme suit :
importer java.util.concurrent.atomic.AtomicReferenceFieldUpdater ;
classe publique CLHLock {
classe statique publique CLHNode {
booléen volatile privé isLocked = true ;
}
@SuppressWarnings("inutilisé")
Queue CLHNode volatile privée ;
private static final ThreadLocal<CLHNode> LOCAL = new ThreadLocal<CLHNode>();
final statique privé AtomicReferenceFieldUpdater<CLHLock, CLHNode> UPDATER = AtomicReferenceFieldUpdater.newUpdater(CLHLock.class,
CLHNode.class, "queue");
verrouillage public vide() {
Nœud CLHNode = new CLHNode();
LOCAL.set(nœud);
CLHNode preNode = UPDATER.getAndSet(this, node);
si (preNode != null) {
tandis que (preNode.isLocked) {
}
preNode = nul ;
LOCAL.set(nœud);
}
}
public void unlock() {
Nœud CLHNode = LOCAL.get();
if (!UPDATER.compareAndSet(this, node, null)) {
node.isLocked = false ;
}
nœud = nul ;
}
}
CLHlock interroge en permanence les variables précurseurs, ce qui le rend impropre à une utilisation sous l'architecture NUMA (dans cette architecture, chaque thread est distribué dans une zone de mémoire physique différente)
MCSLock parcourt les nœuds des variables locales. Il n'y a aucun problème avec CLHlock.
Copiez le code comme suit :
importer java.util.concurrent.atomic.AtomicReferenceFieldUpdater ;
classe publique MCSLock {
classe statique publique MCSNode {
MCSNode volatile ensuite ;
booléen volatile isLocked = true ;
}
private static final ThreadLocal<MCSNode> NODE = new ThreadLocal<MCSNode>();
@SuppressWarnings("inutilisé")
file d'attente MCSNode volatile privée ;
final statique privé AtomicReferenceFieldUpdater<MCSLock, MCSNode> UPDATER = AtomicReferenceFieldUpdater.newUpdater(MCSLock.class,
MCSNode.class, "file d'attente");
verrouillage public vide() {
MCSNode currentNode = nouveau MCSNode();
NODE.set(currentNode);
MCSNode preNode = UPDATER.getAndSet(this, currentNode);
si (preNode != null) {
preNode.next = currentNode;
while (currentNode.isLocked) {
}
}
}
public void unlock() {
MCSNode currentNode = NODE.get();
si (currentNode.next == null) {
if (UPDATER.compareAndSet(this, currentNode, null)) {
} autre {
while (currentNode.next == null) {
}
}
} autre {
currentNode.next.isLocked = false;
currentNode.next = null ;
}
}
}
Du point de vue du code, CLH est plus simple que MCS.
La file d'attente CLH est une file d'attente implicite et n'a pas de véritables attributs de nœud successeur.
La file d'attente MCS est une file d'attente explicite avec de vrais attributs de nœud successeur.
Le verrou par défaut utilisé en interne par JUC ReentrantLock est le verrou CLH (il existe de nombreuses améliorations, comme le remplacement des verrous tournants par des verrous bloquants, etc.).
(Fin du texte intégral)