ProgrammationDéveloppeur backend Java

Comment implémenter le motif Singleton en Java en tenant compte de la multi-threading et de la sérialisation ? Quels sont les nuances d'implémentation ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

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 :

  1. Implémentation Thread-safe (Double-checked Locking) : Souvent utilisée pour une initialisation paresseuse.
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; } }
  1. Enum Singleton : La meilleure façon en termes de protection contre la sérialisation, la réflexion et la multi-threading.
enum EnumSingleton { INSTANCE; // méthodes }
  1. Problèmes de sérialisation : Un Singleton classique peut perdre son unicité après la sérialisation/désérialisation. Pour un fonctionnement correct, il faut ajouter la méthode readResolve() :
private Object readResolve() { return getInstance(); }

Question piège

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.