Klasik Singleton, yalnızca bir nesne örneği oluşturulmasını garanti eder. Java'da birden fazla uygulama yolu vardır, ancak çoklu iş parçacığı ve serileştirme göz önüne alındığında dikkate alınması gereken nüanslar şunlardır:
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; // metodlar }
private Object readResolve() { return getInstance(); }
Thread-safe Singleton için getInstance() metodunu synchronized kullanmak yeterli midir?
Cevap: Evet, ancak bu yaklaşım performans kaybına yol açar çünkü synchronized, getInstance'a her erişimde çağrılır. Daha etkili olanı, instance için Double-checked Locking + volatile kullanımı veya Singleton uygulamak için Enum kullanmaktır.
Verimsiz kod örneği:
public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }
Hikaye
Finansal sistemde Singleton uygulaması yaparken, double-checked locking'de instance referansına
volatileeklemeyi unuttular. Sonuç olarak, yüksek yük altında iki sınıf örneğinin rastgele oluşturulmasına yol açtı ve bu, raporlamanın tutarsızlığına yol açtı.
Hikaye
Kayıt kütüphanesinde Singleton'ı özel bir statik nesne ile uyguladılar, ancak serileştirme ve sonrasında serileştirme (örneğin, küme ortamında) birden fazla örnek oluşturdu. Problem, readResolve() eklenerek çözüldü.
Hikaye
Analitik sistemde geliştirici, thread-safe Singleton’ı synchronized getInstance() ile uyguladı. Yüksek yüklü bir sistemde zirve anında performansda keskin bir düşüş meydana geldi, araştırıldığında: getInstance() çağrıları (bir saniyede binlerce kez) gereksiz bir şekilde senkronizasyon nedeniyle birbirini engelliyordu, oysa başlatmanın yalnızca bir kez yapılması gerekiyordu.