Aller au contenu

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 :

  1. lire la valeur
  2. l’incrémenter
  3. é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 volatile suffit
  • 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.


Voir aussi