synchronized est un mot-clé qui permet d'effectuer un accès thread-safe aux sections de code critiques. Il peut être utilisé soit pour une méthode (par exemple, public synchronized void foo()), soit pour un bloc de code (synchronized(obj) { ... }). Lorsque un thread entre dans un bloc synchronized, il obtient le moniteur (lock) de l'objet. Tant que le moniteur est occupé, d'autres threads ne peuvent pas entrer dans un autre bloc synchronized utilisant le même objet comme moniteur.
Caractéristiques :
this, sur un objet statique (par exemple, sur la classe) ou sur un objet arbitraire.public class Counter { private int count; private final Object lock = new Object(); // objet privé public void increment() { synchronized(lock) { count++; } } }
Pourquoi est-il préférable d'utiliser un lock privé ? Parce que si l'on se synchronise sur un objet public (par exemple, sur this ou sur une chaîne publique), du code externe peut également acquérir ce moniteur, entraînant des deadlocks ou un fonctionnement incorrect.
Question : Que se passe-t-il si on se synchronise sur un objet de type String contenant une valeur fixe ?
Réponse : Les chaînes en Java sont internalisées (ce sont les mêmes objets pour des littéraux identiques). Si on se synchronise sur une chaîne de type synchronized("lock"), on peut accidentellement se croiser avec un autre code qui se synchronise sur le même littéral, ce qui entraîne un blocage inattendu entre des parties totalement différentes du programme.
synchronized("LOCK") { ... }
Histoire
Dans un système de trading multithread, des objets publics étaient utilisés pour la synchronisation, et du code externe a pu acquérir le lock, ce qui a entraîné des deadlocks temporaires entre des threads de différents modules et des temps d'arrêt sur le marché.
Histoire
Un jeune développeur a synchronisé l'accès à une collection sur un littéral de chaîne. Une autre partie du code s'est également synchronisée sur une chaîne avec la même valeur. Ces threads se sont mis en file d'attente les uns derrière les autres, ce qui a provoqué un ralentissement drastique de la logique métier.
Histoire
Pour la synchronisation, un nouvel objet a été choisi chaque fois :
synchronized(new Object()) { ... }. En conséquence, la synchronisation ne fonctionnait pas du tout, et différents threads avaient accès simultané aux données. Cela n'a été découvert qu'au cours des tests de charge.