programowanieProgramista backend w Javie

Jak zaimplementować wzorzec Singleton w Javie z uwzględnieniem wielowątkowości i serializacji? Jakie są niuanse implementacji?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź

Klasyczny Singleton gwarantuje stworzenie tylko jednej instancji obiektu. W Javie istnieje kilka sposobów implementacji, ale z uwzględnieniem wielowątkowości i serializacji należy wziąć pod uwagę niuanse:

  1. Wielowątkowa implementacja (Double-checked Locking): Często używana do leniwej inicjalizacji.
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: Najlepszy sposób pod względem ochrony przed serializacją, refleksją i wielowątkowością.
enum EnumSingleton { INSTANCE; // metody }
  1. Problemy z serializacją: Zwykły Singleton po serializacji/deserializacji może stracić unikalność instancji. Aby działał poprawnie, należy dodać metodę readResolve():
private Object readResolve() { return getInstance(); }

Pytanie z podstępem

Czy wystarczy używać metody synchronized getInstance() dla thread-safe Singleton?

Odpowiedź: Tak, ale takie podejście prowadzi do spadku wydajności, ponieważ synchronized jest wywoływana przy każdym odwołaniu do getInstance. Bardziej efektywne są Double-checked Locking + volatile dla instancji, lub użycie Enum do implementacji Singletona.

Przykład nieefektywnego kodu:

public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }

Historia

W systemie finansowym podczas implementacji singltona zapomniano dodać volatile do referencji instance w double-checked locking. W efekcie przy wysokich obciążeniach występowały losowe przypadki tworzenia dwóch instancji klasy, co prowadziło do inkonsystencji raportów.


Historia

W bibliotece logowania zrealizowano Singleton poprzez prywatny statyczny obiekt, jednak przy serializacji i późniejszej deserializacji (np. w środowisku klastrowym) powstawało kilka instancji. Problem rozwiązano przez dodanie readResolve().


Historia

W systemie analitycznym programista zaimplementował thread-safe Singleton poprzez synchronized metodę getInstance(). W systemie o dużym obciążeniu w momencie szczytu nastąpił nagły spadek wydajności, okazało się, że wywołania getInstance() (tysiące razy na sekundę) blokowały się nawzajem z powodu niepotrzebnej synchronizacji, chociaż inicjalizacja potrzebna była tylko raz.