Synchronized : ce que c'est, principe de fonctionnement et utilisation en Java

Auteur : IT Sectr Publié le : 2026-03-19 Temps de lecture : 8 min

Synchronized est un mécanisme de synchronisation intégré au langage Java qui fournit un accès exclusif aux sections critiques de code. Selon Oracle, 2024, le modificateur synchronized garantit qu'un seul thread peut exécuter la méthode ou le bloc marqué à un moment précis. Ce mécanisme repose sur les moniteurs, un concept fondamental des systèmes d'exploitation qui assure le bon fonctionnement des applications multithread de tous niveaux de complexité.

Points clés

  • Synchronized — mot-clé Java pour un accès thread-safe aux données.
  • Moniteur d'objet — le mécanisme interne sur lequel repose la synchronisation.
  • Méthode synchronized verrouille toute la méthode au niveau de l'instance ou de la classe.
  • Bloc synchronized permet de synchroniser seulement une partie du code.
  • Deadlock — l'un des principaux problèmes de la synchronisation imbriquée.

Qu'est-ce que synchronized ?

Synchronized est un mot-clé en Java qui garantit qu'un seul thread exécute une section protégée de code à un moment donné, empêchant la corruption de données lors de l'accès concurrent. Il est apparu dans la première version de Java et reste le moyen le plus simple d'assurer la thread safety pour les développeurs de tout niveau.

Définition et rôle en Java

Le modificateur synchronized résout deux tâches : l'exclusion mutuelle et la visibilité des modifications. Lorsqu'un thread sort d'un bloc synchronized, toutes les modifications sont garanties visibles pour les autres threads qui entrent dans un bloc synchronisé sur le même objet.

Synchronized peut être appliqué à une méthode entière ou à un bloc de code arbitraire en spécifiant un objet moniteur. Dans les deux cas, la JVM insère les instructions monitorenter et monitorexit au niveau du bytecode.

Raisons de son apparition

Dans les applications multithread sans synchronisation, une condition de course (race condition) se produit lorsque deux threads modifient simultanément les mêmes données, entraînant des résultats imprévisibles. Synchronized est devenu le premier et principal outil de Java pour lutter contre ce problème, offrant une syntaxe déclarative simple accessible à tout développeur.

Comment fonctionne synchronized ?

Le mécanisme synchronized est basé sur le concept de moniteur, une primitive de synchronisation de haut niveau intégrée dans chaque objet Java. Le moniteur est associé à un objet lors de la première utilisation d'un bloc synchronized sur celui-ci.

Moniteur d'objet

Chaque objet en Java a un moniteur associé. Lorsqu'un thread entre dans un bloc synchronized, il acquiert le moniteur de l'objet. Si le moniteur est déjà occupé par un autre thread, le thread se bloque jusqu'à sa libération. En bytecode, cela correspond à la paire d'instructions monitorenter et monitorexit.

États de verrouillage (biased locking)

La JVM optimise synchronized à travers plusieurs niveaux : biased locking (verrouillage biaisé) pour l'accès mono-thread, lightweight locking (verrouillage léger) pour une faible contention, et heavyweight locking (verrouillage lourd) pour une contention intense avec participation du système d'exploitation. Ces niveaux améliorent les performances sans modifier le code.

java
class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public synchronized int getCount() {
        return count;
    }
}

Règle happens-before

Synchronized établit une relation happens-before : toutes les actions dans un thread avant de sortir d'un bloc synchronized sont visibles pour un autre thread après être entré dans un bloc synchronisé sur le même objet. Cela garantit non seulement l'exclusion mutuelle mais aussi la cohérence des données pour tous les threads.

Méthode synchronized vs bloc

Java offre deux façons d'appliquer synchronized : au niveau de la méthode et au niveau du bloc. Le choix entre eux affecte les performances et la granularité de la synchronisation.

Méthode synchronized

Marquer une méthode avec le modificateur synchronized la synchronise automatiquement sur l'instance courante (pour les méthodes d'instance) ou sur l'objet Class (pour les méthodes statiques). C'est le moyen le plus simple d'assurer l'exclusion mutuelle, mais il est souvent excessif si la section critique ne constitue qu'une petite partie de la méthode et que le reste du code ne nécessite pas de synchronisation.

Bloc synchronized

Un bloc synchronized offre un contrôle précis : vous spécifiez l'objet moniteur et synchronisez uniquement la section de code nécessaire, laissant le reste de la méthode en dehors du verrouillage. Cela minimise le temps de détention du moniteur et améliore les performances globales de l'application dans un environnement multithread, car d'autres threads peuvent exécuter du code non lié en parallèle sans attendre la libération du moniteur.

java
class DataProcessor {
    private final Object lock = new Object();

    public void process() {
        // code hors de la section critique - sans synchronisation
        prepareData()

        synchronized (lock) {
            // seul ce bloc est protégé
            updateSharedState()
        }

        // suite sans verrouillage
        cleanup()
    }
}
CritèreMéthode synchronizedBloc synchronized
Moniteurthis (instance) ou Classn'importe quel objet
Granularitéméthode entièreseulement le code nécessaire
Lisibilitéélevéemoyenne
Performanceinférieure pour les grandes méthodessupérieure pour les petites sections critiques

Synchronized dans Android

Dans le développement Android, synchronized est largement utilisé pour protéger SharedPreferences, l'accès aux bases de données et les composants d'interface utilisateur. Cependant, son utilisation sur le thread principal est fortement déconseillée en raison du risque de blocage de l'interface.

Utilisation avec SharedPreferences

SharedPreferences dans Android offre une sécurité de base entre les threads, mais lors de l'édition depuis plusieurs threads via Editor, une synchronisation externe peut être nécessaire. Un bloc synchronized avec un objet de verrouillage séparé garantit la cohérence des modifications.

kotlin
class PreferencesManager(private val prefs: SharedPreferences) {
    private val lock = Any()

    fun writeToken(token: String) {
        synchronized (lock) {
            prefs.edit()
                .putString("auth_token", token)
                .apply()
        }
    }
}

Limitations dans les applications Android

La principale limitation de synchronized sur Android est le blocage de thread. Contrairement aux coroutines avec Mutex, synchronized bloque complètement le thread système. Sur le thread principal, cela provoque une ANR. Dans le développement Android moderne, il est recommandé de remplacer synchronized par des coroutines (suspend Mutex) ou des types atomiques (AtomicInteger).

Alternatives à synchronized

Java et Kotlin modernes offrent plusieurs alternatives à synchronized, chacune résolvant les mêmes problèmes avec moins de limitations ou de meilleures performances.

Lock de java.util.concurrent

L'interface Lock avec les implémentations ReentrantLock et ReadWriteLock fournit des timeouts, une attente interruptible et plusieurs files Condition. Elle est plus flexible que synchronized mais nécessite une libération explicite dans finally, ce qui augmente le risque d'erreur en cas d'oubli du unlock.

Classes atomiques

AtomicInteger, AtomicLong, AtomicReference et d'autres classes utilisent des algorithmes Lock-Free basés sur CAS (Compare-And-Swap). Elles sont significativement plus rapides que synchronized dans les scénarios de contention modérée car elles ne bloquent pas les threads mais effectuent des tentatives optimistes sans nécessiter de changement de contexte du noyau du système d'exploitation.

ThreadLocal et sécurité des threads

ThreadLocal offre une approche alternative : chaque variable ThreadLocal est isolée au sein d'un seul thread et ne nécessite pas de synchronisation pour la lecture et l'écriture. Cela élimine complètement le besoin de synchronized pour les données qui ne doivent pas être partagées entre les threads. ThreadLocal est activement utilisé dans les frameworks (Spring, Hibernate) pour stocker le contexte des transactions et des sessions.

Coroutines et approche compose

Dans les projets Kotlin pour Android, une alternative à synchronized est Mutex de kotlinx.coroutines. Il ne bloque pas le thread du système d'exploitation mais suspend la coroutine jusqu'à la libération du verrou, ce qui permet une utilisation efficace des threads du pool et évite les ANR lors de longues attentes de libération de ressource.

kotlin
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

val mutex = Mutex()
var counter = 0

suspend fun safeIncrement() {
    mutex.withLock {
        counter++
    }
}

Performance de synchronized

Les performances de synchronized ont considérablement changé dans les versions récentes de Java. Avant, il était considéré comme un mécanisme « lourd », mais les JVM modernes ont éliminé la plupart des surcharges grâce aux optimisations avancées du compilateur JIT. Voyons en détail comment la machine virtuelle accélère le code synchronisé à l'exécution.

Biased Locking et Lock Coarsening

Le compilateur JIT de la JVM applique plusieurs optimisations : biased locking élimine la synchronisation si le verrou est toujours acquis par le même thread ; lock coarsening fusionne les blocs synchronized adjacents en un seul ; lock elimination supprime la synchronisation si l'objet n'est accessible que par un seul thread. Ces optimisations rendent synchronized pratiquement gratuit en cas de faible contention.

Mesure de la contention et choix de l'optimisation

La JVM détermine le niveau de contention pour chaque objet : en l'absence de contention, le biased locking est activé ; lorsqu'un second thread apparaît, le verrou passe en mode léger avec attente active (spin) ; et ce n'est qu'en cas d'attente prolongée qu'il escalade vers le mode lourd avec un mutex système. Cette escalade se produit automatiquement, et le développeur n'a pas besoin de choisir manuellement une stratégie.

Comparaison avec Lock et les classes atomiques

Dans les benchmarks modernes (Java 17+), synchronized montre des performances comparables à ReentrantLock avec une contention faible et modérée. En cas de forte contention, Lock peut avoir l'avantage grâce à une file d'attente plus efficace avec prise en charge des timeouts et des interruptions. Pour les systèmes à charge élevée où la contention est constante, ReentrantLock en mode fair offre un comportement plus prévisible.

Les classes atomiques (AtomicInteger, AtomicReference) restent les plus rapides pour les compteurs et indicateurs simples grâce à l'implémentation Lock-Free basée sur CAS. Elles ne bloquent pas du tout les threads : en cas de conflit, l'opération se répète simplement dans une boucle. Cela donne un gain de performance de 3 à 5 fois par rapport à synchronized sur les opérations d'incrémentation de compteur avec 4 à 8 threads.

Questions fréquentes

Quelle est la différence entre synchronized et volatile ?

Synchronized fournit à la fois l'exclusion mutuelle et la visibilité. Volatile ne garantit que la visibilité des modifications : l'écriture dans une variable volatile est visible par tous les threads mais n'empêche pas la modification simultanée, c'est-à-dire qu'elle ne protège pas contre les conditions de course.

Est-ce que synchronized peut provoquer un deadlock ?

Oui, un deadlock est possible avec une synchronisation imbriquée utilisant un ordre de moniteurs différent. Par exemple, un thread appelle synchronized(a) { synchronized(b) }, tandis qu'un autre appelle synchronized(b) { synchronized(a) }. Évitez les blocs synchronized imbriqués ou fixez un ordre de moniteurs cohérent.

Qu'est-ce qu'un moniteur en Java ?

Un moniteur est un mécanisme de synchronisation associé à chaque objet Java. Il garantit qu'un seul thread exécute du code synchronized sur cet objet. Le moniteur comprend un verrou, une file d'attente et un ensemble de threads attendant une notification via wait/notify.

Est-ce que Lock est plus rapide que synchronized ?

Dans les versions modernes de Java (17+), synchronized n'est pas en reste face à Lock en termes de performances grâce aux optimisations JIT (biased locking, lock coarsening). Lock est préféré non pas pour la vitesse mais pour les fonctionnalités supplémentaires : timeouts, attente interruptible et plusieurs files Condition.

Comment fonctionne synchronized avec les méthodes statiques ?

Une méthode statique synchronized utilise le moniteur de l'objet Class de la classe donnée, et non de l'instance. Cela signifie que la synchronisation s'applique à toutes les instances de la classe. Les méthodes synchronized non statiques et statiques utilisent des moniteurs différents et ne se bloquent pas mutuellement.

Résumé

  • Synchronized — mécanisme de synchronisation intégré de Java basé sur les moniteurs.
  • Moniteur d'objet — structure interne de la JVM garantissant l'exclusion mutuelle.
  • Méthodes wait/notify sont utilisées uniquement à l'intérieur de blocs ou méthodes synchronized.
  • Bloc synchronized est préférable à une méthode grâce à une granularité de synchronisation plus fine.
  • Happens-before garantit la visibilité des modifications entre les threads lors de la synchronisation sur le même objet.
  • Deadlock — le risque principal avec la synchronisation imbriquée utilisant un ordre de moniteurs différent.
  • Alternatives — Lock, classes atomiques et coroutines avec suspending Mutex.

Nous développerons une application mobile clé en main

IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.

Discuter du projet

Lisez aussi