Le Singleton classique garantit la création d'une seule instance d'objet. En Java, il existe plusieurs manières de l'implémenter, mais en tenant compte de la multi-threading et de la sérialisation, il faut considérer les nuances :
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; } }
enum EnumSingleton { INSTANCE; // méthodes }
private Object readResolve() { return getInstance(); }
Est-il suffisant d'utiliser la méthode synchronisée getInstance() pour un Singleton thread-safe ?
Réponse : Oui, mais cette approche entraîne une diminution des performances, car synchronized est appelé à chaque appel à getInstance. Double-checked Locking + volatile pour instance ou l'utilisation d'Enum pour l'implémentation du Singleton est plus efficace.
Exemple de code inefficace :
public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }
Histoire
Dans le système financier, lors de l'implémentation du Singleton, on a oublié d'ajouter
volatileà la référence instance dans le double-checked locking. En conséquence, sous des charges élevées, des cas aléatoires de création de deux instances de classe sont survenus, entraînant une incohérence des reportings.
Histoire
Dans la bibliothèque de journalisation, le Singleton a été implémenté via un objet statique privé, mais lors de la sérialisation et de la désérialisation subséquente (par exemple, dans un environnement de cluster), plusieurs instances apparaissaient. Le problème a été résolu en ajoutant readResolve().
Histoire
Dans le système d'analyse, le développeur a implémenté un Singleton thread-safe via la méthode synchronisée getInstance(). Dans un système à forte charge, au moment du pic, il y a eu une chute brutale des performances. Il s'est avéré que les appels à getInstance() (des milliers de fois par seconde) se bloquaient les uns les autres en raison d'une synchronisation inutile, même si l'initialisation n'était nécessaire qu'une seule fois.