Maison > Java > javaDidacticiel > le corps du texte

Exemple de Java implémentant une programmation sans verrouillage d'instructions cas

黄舟
Libérer: 2017-09-15 11:07:21
original
1529 Les gens l'ont consulté

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;
  }
}
Copier après la connexion

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;
  }
Copier après la connexion

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;
Copier après la connexion
Il s'agit d'un attribut défini en interne, utilisé pour sauvegarder les valeurs. Puisqu'il est de type volatile, il peut assurer la visibilité entre les threads et l'atomicité de lecture et d'écriture. . .

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;
  }
}
Copier après la connexion
La fonction de cette méthode est d'ajouter un delta à la valeur actuelle, ici Vous pouvez voir qu'il n'y a pas de verrou dans l'ensemble de la méthode. Ce code est en fait une méthode pour implémenter un compteur sans verrouillage en Java. La méthode compareAndSet est définie comme suit :


<. 🎜>

Puisque la méthode non sécurisée est appelée, vous ne pouvez rien y faire. En fait, vous devriez pouvoir deviner que la JVM appelle l'instruction CAS du processeur lui-même pour implémenter des opérations atomiques. . .
public final boolean compareAndSet(int expect, int update) {
  return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
Copier après la connexion


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. . .

Ce qui précède a été complété pour implémenter un compteur sans verrouillage en Java. Voyons ensuite comment implémenter une pile sans verrouillage et collez le code directement. Le code est imité de "JAVA Concurrent Programming in Action". " :



D'accord, le code ci-dessus implémente une pile sans verrouillage, simple. . . Dans un environnement concurrent, les structures de données sans verrouillage peuvent bien mieux évoluer que les verrous. . .
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;
    }
  }
}
Copier après la connexion
Quand on parle de programmation sans verrouillage, nous devons mentionner les files d'attente sans verrouillage. En fait, l'implémentation de files d'attente sans verrouillage a été fournie dans la bibliothèque concurrente : ConcurrentLinkedQueue.



Cette méthode est utilisée pour ajouter des éléments à la fin de la file d'attente. Ici, vous pouvez voir qu'il n'y a pas de verrou pour l'algorithme sans verrouillage spécifique, Michael-Scott l'a proposé. Un algorithme de liste chaînée non bloquant. . . Pour voir comment cela fonctionne spécifiquement, vous pouvez consulter « Programmation simultanée JAVA en pratique » pour une introduction plus détaillée.
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;
  }
}
Copier après la connexion

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!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal