«Synchronized» en Java : ce que c'est et comment ça fonctionne avec des exemples

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

« Que signifie synchronized ? » — la question qui hante un junior sur deux

\n\n

Si vous avez déjà écrit une application multithread en Java, vous avez sûrement été confronté à cette situation : deux threads modifient simultanément une même variable, et le résultat — des bugs qui ne se reproduisent qu'une fois sur cent exécutions. Cela vous dit quelque chose ? C'est exactement pour cela que le mot-clé synchronized existe. Mais que fait-il réellement ? Comment fonctionne-t-il « sous le capot » ? Et surtout — quand l'utiliser, et quand vaut-il mieux s'en passer ?

\n\n

Dans cet article, nous ne nous contenterons pas de répéter la documentation. Nous analyserons une question populaire de Stack Overflow (celle qui a recueilli des milliers de vues), nous l'adapterons à un format éducatif et montrerons une comparaison des approches : synchronisation au niveau de la méthode, au niveau du bloc, et les alternatives de java.util.concurrent. C'est parti !

\n\n

Le problème : la course aux threads (Race Condition)

\n\n

Imaginez : vous avez un compteur que deux threads incrémentent. Le code est simple, mais le résultat est inattendu.

\n\n
public class Counter {\n    private int count = 0;\n\n    public void increment() {\n        count++;\n    }\n\n    public int getCount() {\n        return count;\n    }\n}
\n\n

Lançons deux threads, chacun effectue 1000 incréments. On s'attendrait à 2000. Mais en réalité — 1987, 1993, ou même 1765. Pourquoi ? Parce que l'opération count++ n'est pas atomique. Elle se compose de trois étapes : lecture → incrémentation → écriture. Les threads « se chevauchent », et une modification est perdue.

\n\n

C'est ce qu'on appelle une race condition — une condition de course. Et synchronized est l'un des principaux outils pour la combattre.

\n\n

Que fait synchronized ? Regardons sous le capot

\n\n

Le mot-clé synchronized en Java garantit l'exclusion mutuelle (mutex) et la visibilité des modifications entre les threads. Lorsqu'un thread entre dans un bloc synchronized, il verrouille le moniteur de l'objet (ou de la classe). Les autres threads qui tentent d'entrer dans un bloc synchronized sur le même moniteur sont bloqués jusqu'à sa libération.

\n\n

Important : synchronized ne fonctionne qu'avec un seul moniteur. Si vous avez deux objets différents — la synchronisation ne fonctionnera pas.

\n\n

Deux niveaux de synchronisation

\n
    \n
  • Méthode synchronisée — verrouille toute la méthode, utilise le moniteur de l'objet (this).
  • \n
  • Bloc synchronisé — verrouille seulement une partie du code, vous pouvez spécifier n'importe quel objet comme moniteur.
  • \n
\n\n

Comparons-les en pratique.

\n\n

Comparaison des approches : méthode vs bloc

\n\n

1. Méthode synchronisée

\n\n

La manière la plus simple — ajouter synchronized à la signature de la méthode.

\n\n
public class SafeCounter {\n    private int count = 0;\n\n    public synchronized void increment() {\n        count++;\n    }\n\n    public synchronized int getCount() {\n        return count;\n    }\n}
\n\n

Avantages :

\n
    \n
  • Code minimal, facile à lire.
  • \n
  • Garantit que toute la méthode est une opération atomique.
  • \n
\n\n

Inconvénients :

\n
    \n
  • Verrouillage « lourd » — même si la méthode ne contient qu'une seule ligne, le thread est bloqué pendant toute la durée d'exécution.
  • \n
  • Réduit les performances si la méthode est longue ou appelée fréquemment.
  • \n
  • Pas flexible — impossible de synchroniser seulement une partie de la logique.
  • \n
\n\n

2. Bloc synchronisé

\n\n

Un contrôle plus fin. Vous décidez vous-même quel objet utiliser comme moniteur.

\n\n
public class SafeCounterWithBlock {\n    private int count = 0;\n    private final Object lock = new Object();\n\n    public void increment() {\n        synchronized (lock) {\n            count++;\n        }\n    }\n\n    public int getCount() {\n        synchronized (lock) {\n            return count;\n        }\n    }\n}

Blogs

Book Recommendations