De klassieke Singleton garandeert de creatie van slechts één exemplaar van een object. In Java zijn er verschillende manieren om dit te implementeren, maar met betrekking tot multithreading en serialisatie moet je rekening houden met de 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; // methodes }
private Object readResolve() { return getInstance(); }
Is het voldoende om de gesynchroniseerde methode getInstance() te gebruiken voor een thread-safe Singleton?
Antwoord: Ja, maar deze benadering leidt tot verminderde prestaties omdat synchronized bij elke aanroep van getInstance wordt aangeroepen. Double-checked Locking + volatile voor instance of het gebruik van Enum voor de implementatie van Singleton is efficiënter.
Voorbeeld van inefficiële code:
public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }
Geschiedenis
In een financieel systeem werd vergeten
volatiletoe te voegen aan de instance-referentie in double-checked locking. Dit leidde op hoge belasting tot willekeurige gevallen van het creëren van twee exemplaren van de klasse, wat resulteerde in inconsistentie in rapportage.
Geschiedenis
In de logboekbibliotheek werd Singleton gerealiseerd via een privé statisch object, maar bij serialisatie en daaropvolgende deserialisatie (bijvoorbeeld in een clusteromgeving) ontstonden er meerdere exemplaren. Het probleem werd opgelost door readResolve() toe te voegen.
Geschiedenis
In een analytisch systeem implementeerde een ontwikkelaar een thread-safe Singleton via de gesynchroniseerde methode getInstance(). In een systeem met hoge belasting ontstond er een scherpe daling van de prestaties op het hoogtepunt; het bleek dat de aanroepen van getInstance() (duizenden keren per seconde) elkaar blokkeerden vanwege onnodige synchronisatie, hoewel initialisatie slechts één keer nodig was.