advanced.java
← Tous les concepts
Concurrence · ch. 9, p. 73

Variables atomiques et compare-and-swap

Les classes atomiques utilisent une instruction matérielle de comparaison-échange pour mettre à jour une valeur sans verrou : lire, calculer, puis échanger seulement si rien n'a changé entre-temps.

count++ est trois opérations — lire, ajouter, écrire — et deux threads peuvent s’entrelacer à l’intérieur. AtomicInteger rend toute la mise à jour indivisible.

AtomicInteger counter = new AtomicInteger();
counter.incrementAndGet();

// Mise à jour atomique arbitraire, retentée jusqu'à ce qu'elle gagne la course
counter.updateAndGet(v -> Math.min(v + 1, MAX));

La boucle de réessai est le point clé : le CAS échoue quand un autre thread est arrivé le premier, et l’opération réessaie simplement avec la nouvelle valeur. Aucun thread ne se bloque jamais, ce qui explique pourquoi les atomiques surpassent les verrous sous contention modérée — et pourquoi ils se dégradent sous forte contention, quand les réessais s’accumulent. LongAdder existe pour ce cas, en répartissant les mises à jour sur plusieurs cellules et en sommant à la lecture.

Une seule variable à la fois

Le CAS ne couvre qu’une seule variable. Deux champs qui doivent changer ensemble ont toujours besoin d’un verrou ou d’un objet immuable échangé atomiquement avec AtomicReference.

Le problème ABA

Une valeur peut passer de A à B puis revenir à A entre votre lecture et votre échange ; le CAS n’y voit aucune différence. AtomicStampedReference attache un compteur de version quand cela compte.

volatile ne suffit pas

volatile garantit la visibilité et l’ordre, pas l’atomicité. Un volatile int perd toujours des incréments.

2 questions sur ce concept S'entraîner