Maison  >  Article  >  Java  >  Analyse des opérations cas en Java

Analyse des opérations cas en Java

黄舟
黄舟original
2017-09-15 10:52:391534parcourir

Cet article analyse les concepts, les principes et l'utilisation des opérations cas dans la programmation Java à travers des exemples. Il a une certaine valeur de référence et les amis dans le besoin peuvent en apprendre davantage.

CAS fait référence à une instruction spéciale largement prise en charge par les processeurs modernes qui fonctionne sur des données partagées en mémoire. Cette instruction effectue des opérations de lecture et d'écriture atomiques sur les données partagées en mémoire.

Une brève introduction au processus de fonctionnement de cette instruction : Tout d'abord, le CPU compare les données à modifier dans la mémoire avec la valeur attendue. Ensuite, lorsque les deux valeurs sont égales, le CPU remplace la valeur en mémoire par la nouvelle valeur. Dans le cas contraire, aucune opération ne sera effectuée. Enfin, le CPU renvoie l'ancienne valeur. Cette série d'opérations est atomique. Bien qu'ils semblent compliqués, ils constituent la raison fondamentale pour laquelle le mécanisme de concurrence Java 5 est meilleur que le mécanisme de verrouillage d'origine. En termes simples, la signification de CAS est "Qu'est-ce que je pense que la valeur d'origine devrait être, si c'est le cas, mettez à jour la valeur d'origine avec la nouvelle valeur, sinon ne la modifiez pas et dites-moi quelle est la valeur d'origine." (Cette description est tirée de "Java Concurrent Programming Practice")

Pour faire simple, CAS a 3 opérandes, la valeur mémoire V, l'ancienne valeur attendue A et la nouvelle valeur à modifier B. Si et seulement si la valeur attendue A et la valeur mémoire V sont identiques, modifiez la valeur mémoire V en B, sinon renvoyez V. C'est une idée de verrouillage optimiste.Il croit qu'aucun autre thread ne le modifiera avant qu'il ne soit modifié;Synchronisé est un verrou pessimiste.Il croit que d'autres threads le modifieront avant qu'il ne soit modifié.Efficacité du verrouillage pessimisteTrès faible.

Regardons un exemple simple :


if(a==b) { 
  a++; 
}

Imaginez simplement que se passerait-il si la valeur de a était modifiée avant de faire a++ ? Est-ce que a++ est toujours exécuté ? La raison de ce problème est que dans un environnement multithread, la valeur de a est dans un état incertain. Les verrous peuvent résoudre de tels problèmes, mais CAS peut également les résoudre sans verrous.


int expect = a; 
if(a.compareAndSet(expect,a+1)) { 
  doSomeThing1(); 
} else { 
  doSomeThing2(); 
}

De cette façon, a++ ne sera pas exécuté si la valeur de a est modifiée.

Selon la méthode d'écriture ci-dessus, après a!=expect, a++ ne sera pas exécuté. Et si nous voulons toujours effectuer une opération a++, cela n'a pas d'importance. 🎜>


while(true) { 
  int expect = a; 
  if (a.compareAndSet(expect, a + 1)) { 
    doSomeThing1(); 
    return; 
  } else { 
    doSomeThing2(); 
  } 
}
En utilisant la méthode d'écriture ci-dessus, une opération a++ est implémentée sans verrouillage. Il s'agit en fait d'un algorithme non bloquant.

Application

Presque la plupart des classes du package java.util.concurrent.atomic utilisent des opérations CAS, prenez AtomicInteger comme exemple, voir Look lors de l'implémentation de plusieurs de ses méthodes principales :



public final int getAndSet(int newValue) { 
  for (;;) { 
    int current = get(); 
    if (compareAndSet(current, newValue)) 
      return current; 
  } 
}
L'explication dans la documentation JDK de la méthode getAndSet est la suivante : définir une valeur donnée de manière atomique et renvoyer l'ancienne valeur. Là où la méthode atomique est reflétée, elle est reflétée dans compareAndSet. Voyons comment compareAndSet est implémenté :


public final boolean compareAndSet(int expect, int update) { 
  return unsafe.compareAndSwapInt(this, valueOffset, expect, update); 
}
Comme prévu, il utilise la classe Unsafe. L'opération CAS est complété.


Regardons comment l'opération a++ est implémentée :


public final int getAndIncrement() { 
  for (;;) { 
    int current = get(); 
    int next = current + 1; 
    if (compareAndSet(current, next)) 
      return current; 
  } 
}
C'est presque exactement la même chose que l'exemple original, et utilise également l'opération CAS pour implémenter l'opération d'incrémentation automatique.


++une opération est similaire à une opération ++, sauf que les résultats de retour sont différents


public final int incrementAndGet() { 
  for (;;) { 
    int current = get(); 
    int next = current + 1; 
    if (compareAndSet(current, next)) 
      return next; 
  } 
}
De plus, java.util .concurrent. La classe ConcurrentLinkedQueue utilise un algorithme non bloquant, qui n'utilise aucun verrou, et est entièrement implémenté sur la base d'opérations CAS. Le fonctionnement CAS peut être considéré comme le fondement du cadre de concurrence JAVA, et la conception de l'ensemble du cadre est basée sur le fonctionnement CAS.

Inconvénients :

1 Problème ABA

Wikipédia donne un exemple vivant -


Vous êtes. à l'aéroport avec une valise pleine d'argent. A ce moment-là, une beauté chaude et sexy arrive Puis elle te taquine de manière très ambiguë, et quand tu ne fais pas attention, elle utilise la même que celle que j'ai échangée avec ta valise. plein d'argent, puis vous êtes parti. Vous avez vu que votre valise était toujours là, alors vous avez pris la valise et êtes allé prendre l'avion.


C'est le problème de l'ABA.

Les opérations CAS peuvent facilement conduire à des problèmes ABA, c'est-à-dire qu'entre l'exécution de a++, a peut avoir été modifié par plusieurs threads, mais il vient de revenir à la valeur d'origine. À ce moment-là, CAS pensera que le. la valeur de a n’a pas changé. a Après avoir marché un moment dehors, vous pouvez garantir que ça n'a rien fait de mal, non ! ! Peut-être pour le plaisir, cela diminue la valeur de b, augmente la valeur de c, etc. De plus, si a est un objet, cet objet peut être nouvellement créé, et a est une référence. Comment, alors il y en a ? encore de nombreux problèmes ici. Il existe de nombreuses façons de résoudre le problème ABA. Vous pouvez envisager d'ajouter un nombre de modifications uniquement lorsque le nombre de modifications reste inchangé et que la valeur de a reste inchangée. , les opérations a++ ne sont effectuées que lorsque les numéros de version sont les mêmes. Ceci est quelque peu similaire au traitement de l'atomicité des transactions !


2. Il consomme plus de ressources CPU et effectuera un travail inutile même s'il n'y a pas de conflit.


3. Cela augmentera la complexité des tests du programme et des problèmes peuvent survenir si vous ne faites pas attention.

Résumé

CAS peut être utilisé pour implémenter des opérations atomiques sans verrous, mais les scénarios d'application doivent être clairement définis. Pour des opérations très simples sans introduire de verrous, vous pouvez envisager d'utiliser des opérations CAS lorsque vous souhaitez réaliser une opération. fonctionnement non bloquant. Il n'est pas recommandé d'introduire CAS dans des opérations complexes, car cela rendrait le programme moins lisible et difficile à tester, et des problèmes ABA surviendraient.

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