Cet article présente principalement l'exemple d'implémentation de programmation sans verrouillage de l'instruction cas en langage Java. Il a une certaine valeur de référence et les amis dans le besoin peuvent en apprendre davantage.
La première fois que vous entrez en contact avec un contenu pertinent, c'est avec le mot-clé volatile. Vous savez qu'il peut assurer la visibilité des variables, et qu'il peut être utilisé pour implémenter des opérations atomiques de lecture et d'écriture. . . Mais volatile ne peut rien faire pour implémenter certaines opérations composites. . . Les représentants les plus typiques sont les opérations d'incrémentation et de décrémentation. . . .
Nous savons que dans un environnement concurrent, le moyen le plus simple d'assurer la cohérence des données est de verrouiller pour garantir qu'un seul thread peut opérer sur les données en même temps. . . . Par exemple, un compteur peut être implémenté de la manière suivante :
public class Counter { private volatile int a = 0; public synchronized int incrAndGet(int number) { this.a += number; return a; } public synchronized int get() { return a; } }
On modifie toutes les opérations avec le mot-clé synchronisé pour assurer un accès synchrone à l'attribut a. . . Cela peut en effet garantir la cohérence d'un dans un environnement concurrent, mais en raison de l'utilisation de verrous, de la surcharge de verrouillage, de la planification des threads, etc., l'évolutivité du programme est limitée, il existe donc de nombreuses implémentations sans verrouillage. . . .
En fait, ces méthodes sans verrouillage utilisent toutes certaines instructions CAS (comparer et changer) fournies par le processeur. Que fait ce CAS ? Vous pouvez utiliser la méthode suivante pour expliquer ce que fait le CAS. La sémantique représentée :
public synchronized int compareAndSwap(int expect, int newValue) { int old = this.a; if (old == expect) { this.a = newValue; } return old; }
Eh bien, l'offre pour la sémantique CAS devrait être très claire à travers le code. Il semble que la plupart des processeurs implémentent désormais la commande CAS atomique. .
D'accord, voyons où CAS est utilisé en Java. Examinons d'abord le type AtomicInteger. Il s'agit d'un type fourni par la bibliothèque de concurrence :
private volatile int value;
Jetons ensuite un coup d'œil à quelques-unes des méthodes les plus couramment utilisées :
public final int addAndGet(int delta) { for (;;) { int current = get(); int next = current + delta; if (compareAndSet(current, next)) return next; } }
<. 🎜>
public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
Fondamentalement, les méthodes importantes de type AtomicInteger sont implémentées sans verrouillage. . Par conséquent, dans un environnement concurrent, l’utilisation de ce type peut offrir de meilleures performances. . .
package concurrenttest; import java.util.concurrent.atomic.AtomicReference; public class ConcurrentStack<e> { AtomicReference<node<e>> top = new AtomicReference<node<e>>(); public void push(E item) { Node<e> newHead = new Node<e>(item); Node<e> oldHead; while (true) { oldHead = top.get(); newHead.next = oldHead; if (top.compareAndSet(oldHead, newHead)) { return; } } } public E pop() { while (true) { Node<e> oldHead = top.get(); if (oldHead == null) { return null; } Node<e> newHead = oldHead.next; if (top.compareAndSet(oldHead, newHead)) { return oldHead.item; } } } private static class Node<e> { public final E item; public Node<e> next; public Node(E item) { this.item = item; } } }
public boolean offer(E e) { checkNotNull(e); final Node<e> newNode = new Node<e>(e); for (Node<e> t = tail, p = t;;) { Node<e> q = p.next; if (q == null) { // p is last node if (p.casNext(null, newNode)) { // Successful CAS is the linearization point // for e to become an element of this queue, // and for newNode to become "live". if (p != t) // hop two nodes at a time casTail(t, newNode); // Failure is OK. return true; } // Lost CAS race to another thread; re-read next } else if (p == q) // We have fallen off list. If tail is unchanged, it // will also be off-list, in which case we need to // jump to head, from which all live nodes are always // reachable. Else the new tail is a better bet. p = (t != (t = tail)) ? t : head; else // Check for tail updates after two hops. p = (p != t && t != (t = tail)) ? t : q; } }
De plus, d'autres méthodes sont effectivement mises en œuvre de manière sans verrouillage.
Enfin, dans la programmation réelle, il est préférable d'utiliser ces implémentations sans verrouillage dans un environnement concurrent, après tout, elles ont une meilleure évolutivité.
Résumé
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!