web-dev-qa-db-fra.com

Comment fonctionne "Compare And Set" dans AtomicInteger?

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();
19
Onki

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:

  1. Le thread 1 appelle get et obtient la valeur 1.
  2. Le thread 1 calcule que next est 2.
  3. Le thread 2 appelle get et obtient la valeur 1.
  4. Le thread 2 calcule que next est 2.
  5. Les deux threads essaient d'écrire la valeur.

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.

19
OldCurmudgeon

for (;;) est une boucle infinie, donc il va simplement réessayer la tentative.

8
Dragan Bozanovic