AtomicInteger
fonctionne avec deux concepts: CAS et volatile
variable.
L'utilisation de la variable volatile
garantit que la valeur actuelle sera visible pour tous les threads et ne sera pas mise en cache.
Mais je suis confus sur le concept CAS (comparer ET définir) qui est expliqué ci-dessous:
public final int getAndIncrement() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return current;
}
}
Ma question est que whatif(compareAndSet(current, next)
renvoie false
? La valeur ne sera-t-elle pas mise à jour? Dans ce cas, que se passera-t-il lorsqu'un thread exécute le cas ci-dessous:
private AtomicInteger count = new AtomicInteger();
count.incrementAndGet();
Les objets atomiques utilisent le mécanisme Compare and Swap pour les rendre atomiques - c'est-à-dire qu'il est possible de garantir que la valeur était comme spécifié et est maintenant à la nouvelle valeur.
Le code que vous avez publié essaie continuellement de définir la valeur actuelle à un de plus qu'avant. N'oubliez pas qu'un autre thread aurait également pu effectuer un get
et essaie de le définir également. Si deux threads s'affrontent pour modifier la valeur, il est possible qu'un des incréments échoue.
Considérez le scénario suivant:
get
et obtient la valeur 1
.next
est 2
.get
et obtient la valeur 1
.next
est 2
.Maintenant, à cause de l'atomique - un seul thread réussira , l'autre recevra false
du compareAndSet
et fera le tour encore.
Si ce mécanisme n'était pas utilisé, il serait tout à fait possible que les deux threads incrémentent la valeur résultant en un seul incrément réellement effectué.
La boucle infinie déroutante for(;;)
ne bouclera vraiment que si plusieurs threads écrivent dans la variable en même temps. Sous une charge très lourde, il peut boucler plusieurs fois, mais il devrait se terminer assez rapidement.
for (;;)
est une boucle infinie, donc il va simplement réessayer la tentative.