Récemment, je lisais un tutoriel, dans lequel je suis tombé sur une déclaration qui dit:.
"La spécification du langage Java garantit que la lecture ou l'écriture d'une variable est une opération atomique (sauf si la variable est de type long
ou double
). Les variables d'opérations de type long
ou double
ne sont atomiques que si elles sont déclarées avec le mot clé volatile
."
AtomicInteger
ou AtomicLong
qui fournit des méthodes telles que getAndDecrement()
, getAndIncrement()
et getAndSet()
qui sont atomiques.
Je me suis un peu confondu avec la déclaration ci-dessus. Pourriez-vous s'il vous plaît préciser quand utiliser AtomicInteger
ou AtomicLong
classes.
Faire a = 28
(avec a
étant une int
) est une opération atomique. Cependant, exécuter a++
n’est pas une opération atomique car il nécessite une lecture de la valeur de a, une incrémentation et une écriture dans a du résultat. Par conséquent, si vous utilisiez a++
pour implémenter un compteur thread-safe, vous pourriez avoir deux threads lisant la valeur simultanément (26 par exemple), puis l’incrémenter et l’écrire simultanément, résultant en 27 au lieu 28.
AtomicInteger résout ce problème en fournissant des opérations atomiques similaires à celles que vous avez énumérées. Dans mon exemple, vous utiliseriez incrementAndGet()
par exemple, ce qui garantirait que la valeur finale est 28 et non 27.
Atomic signifie que l'opération se termine sans aucune possibilité qu'il se passe quelque chose entre les deux. par exemple. getAndDecrement (), sur AtomicInteger, garantit que la variable est retournée ET est décrémentée en même temps.
S'il ne s'agissait pas d'une opération atomique, il serait possible que la valeur soit décrémentée (par exemple de 3 à 2), puis modifiée par un autre thread (par exemple en la modifiant de 2 à 5), puis renvoyée sous la forme 5.
Vous avez besoin de AtomicInteger
si vous devez lire une variable et écrire un résultat en fonction de la valeur de lecture. Par exemple, i++
lit i
(par exemple 3
) et écrit i+1
(par exemple 4
). Un thread peut être interrompu entre temps et trois autres threads incrémentent également i
. Maintenant que nous avons récupéré, i
a en fait la valeur 6
mais notre thread écrit toujours 4
, en fonction de ce qu'il a lu auparavant.
AtomicInteger.getAndIncrement
s'assure que vous êtes pas interrompu et donc toujours incrémenté correctement. De plus, le résultat est toujours vidé dans la mémoire, alors qu'une variable i
non volatile pourrait ne pas être vidée en mémoire. Dans ce cas, d'autres threads pourraient même ne pas voir les modifications.
Je pense que ce que cela signifie, c'est qu'une opération longue et à double lecture est atomique et qu'une opération d'écriture est atomique. Mais une lecture + écriture n'est pas atomique.
volatile long num;
num = num+1
Ce qui précède n'est pas thread-safe. La lecture et l’écriture sont deux opérations distinctes. Chacun de ceux-ci est garanti d'être atomique, mais l'expression entière ne l'est pas.
Pour le rendre thread-safe, vous devez utiliser AtomicLong et utiliser la fonction getAndIncrement.
Vous utilisez int ou long en fonction de la limite supérieure/inférieure de la plage de nombres avec laquelle vous traitez. Veuillez ne pas mélanger le comportement non atomique de long avec AtomicLong. Tout ce que vous avez écrit ci-dessus est correct mais vous mélangez probablement les deux concepts. AtomicXXX sont plus utiles dans les cas où vous effectuez une opération de type "comparaison & définition". Par exemple, même si int peut être modifié/lu de manière atomique, le code suivant sera incorrect dans un environnement multithread:
int i =10
..
..
..
if(i == 10) i++;
dans un environnement multithread, deux threads peuvent accéder à ce code de manière atomique et à la valeur mise à jour de i, le rendant ainsi cohérent. SO traiter de telles situations normalement vous gardez le code "if (i == 10) i ++;" avec bloc synchronisé. Cependant, la classe AtomicInteger fournit l'API permettant de réaliser de telles choses sans utiliser de blocs synchronisés plus lents. Même est le cas des API AtmoicLong
l'atomicité d'une opération est requise lorsque vous modifiez une variable. faire int a = 10;
est une opération atomique mais ce n'est pas celle qui vous posera le problème. les opérations donnant des problèmes sont généralement des opérations de mutation telles que a++
ou a = a + 2;
et ainsi de suite.
Les spécifications Java garantissent que «lire» et «écrire» sont des opérations atomiques et non leurs combinaisons. Ainsi, une opération qui "lit, ajoute 1 puis écrit le résultat" n'est pas atomique conformément à la spécification. de telles opérations sont appelées opérations composées et elles doivent généralement être atomiques dans le contexte de leur utilisation dans notre code.
Les types atomiques aident à résoudre ce problème. utiliser incrementAndget () sur un type atomique make 'reads, ajoute 1, puis réécrit le résultat et lit le nouveau résultat' une seule opération atomique en contexte afin de sécuriser le thread.
J'espère que cela t'aides. En passant, vous devriez lire cet article ( http://walivi.wordpress.com/2013/08/24/concurrency-in-Java-a-beginners-introduction/ ) sur les bases de la concurrence et des threads. ça explique magnifiquement ce genre de choses.