Java — Race Condition (Un problème critique en concurrence)
Les race conditions font partie des bugs les plus dangereux en programmation concurrente. Elles apparaissent souvent sans prévenir et peuvent produire des résultats incohérents.
Définition simple
Une race condition se produit lorsque :
plusieurs threads accèdent ou modifient une même donnée en même temps sans synchronisation
➡️ Résultat : imprévisible
Exemple classique
int count = 0;
Thread t1 = new Thread(() -> count++);
Thread t2 = new Thread(() -> count++);
Résultat attendu :
count = 2
Résultat réel possible :
count = 1
count = 2
💥 Pourquoi ça arrive ?
L’opération :
count++;
n’est pas atomique.
Elle se décompose en 3 étapes :
- lire la valeur
- l’incrémenter
- écrire la nouvelle valeur
Si deux threads exécutent ça en même temps → conflit
Visualisation
- Thread 1 lit
0 - Thread 2 lit
0 - Thread 1 écrit
1 - Thread 2 écrit
1
Résultat final : 1
Pourquoi c’est dangereux
- le programme ne plante pas
- le bug est intermittent
- difficile à reproduire
- difficile à corriger
Dans des systèmes critiques (finance, trading…), c’est un problème majeur
🛠️ Solutions
synchronized
synchronized(this) {
count++;
}
garantit qu’un seul thread exécute le bloc à la fois
AtomicInteger
AtomicInteger count = new AtomicInteger(0);
count.incrementAndGet();
solution moderne opération atomique sans blocage explicite
Lock (plus avancé)
Lock lock = new ReentrantLock();
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
plus flexible que synchronized
nécessite une gestion correcte (toujours libérer le lock)
Lien avec volatile
volatile :
- ✔️ garantit la visibilité
- ❌ ne garantit pas l’atomicité
Une race condition :
- vient d’un problème d’atomicité
➡️ Donc volatile ne suffit pas
Pièges fréquents
- croire que
count++est sûr - croire que
volatilesuffit - sous-estimer les bugs concurrents
Questions classiques
Q1
count++ est-il thread-safe ?
Non
Q2
volatile int count; count++ est-il sûr ?
Non
Q3
Quelle solution utiliser ?
synchronized, AtomicInteger, ou Lock
Q4
Pourquoi c’est dangereux ? Résultat imprévisible
À retenir
- une race condition = accès concurrent non contrôlé
- les opérations non atomiques sont la cause principale
- il faut synchroniser l’accès aux données partagées
🧾 En résumé
- plusieurs threads modifient une donnée → risque de conflit
- les opérations comme
++ne sont pas atomiques - le résultat peut être incorrect sans erreur visible
- solutions : synchronisation, classes atomiques, locks
Les race conditions sont invisibles mais critiques : il faut les anticiper dès la conception.