Le concept d'un singleton en Java est une classe qui ne peut avoir qu'une instance. Cette instance unique peut alors être utilisée dans d'autres parties de votre code sans avoir à créer une nouvelle Classe de l'objet. C'est particulièrement utile lorsque vous voulez une seule instance globale à travers toute votre application, comme un gestionnaire de configuration ou un pool de connexions de base de données.
La question soulève un problème important dans l'implémentation d'un singleton : la sécurité des threads. Si plusieurs threads essaient de créer une instance de la classe singleton en même temps, il est possible qu'ils obtiennent chacun une instance distincte - brisant ainsi le principe du singleton. Pour éviter cela, nous devons garantir que notre classe singleton est "thread-safe", c’est-à-dire qu'elle se comporte comme prévu lorsqu'elle est utilisée dans un environnement multithread.
La meilleure manière de créer un singleton thread-safe en Java est d'utiliser le modèle de verrouillage double-vérifié. Voici comment cela fonctionne:
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
Dans ce code:
getInstance()
vérifie d'abord si une instance du singleton a déjà été créée (if (instance == null)
). Si c'est le cas, elle retourne simplement cette instance.Singleton.class
. Cela signifie que seul un thread à la fois peut exécuter ce bloc de code.if (instance == null)
). C'est parce qu'un autre thread a pu créer une instance alors que le thread actuel attendait pour entrer dans le bloc synchronisé.instance
.Le mot-clé volatile
est essentiel ici. Il assure que les changements apportés à instance
sont immédiatement visibles par tous les autres threads. Sans volatile
, il est possible qu'un autre thread voie instance
comme nul même après qu'il a été initialisé.
Ainsi, le verrouillage Double-Vérifié garantit une sécurité totale en environnement multi-thread tout en minimisant le coût de synchronisation, puisque le bloc synchronisé n'est exécuté qu'au moment de la première initialisation.