Классический Singleton гарантирует создание только одного экземпляра объекта. В Java есть несколько способов реализации, но с учётом многопоточности и сериализации нужно учесть нюансы:
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; // методы }
private Object readResolve() { return getInstance(); }
Достаточно ли использовать synchronized метод getInstance() для thread-safe Singleton?
Ответ: Да, но такой подход приводит к снижению производительности, потому что synchronized вызывается при каждом обращении к getInstance. Более эффективен Double-checked Locking + volatile для instance, или использование Enum для реализации Singleton.
Пример неэффективного кода:
public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }
История
В финансовой системе при реализации синглтона забыли добавить
volatileк ссылке instance в double-checked locking. В результате на высоких нагрузках возникали рандомные случаи создания двух экземпляров класса, что привело к неконсистентности отчетности.
История
В библиотеке логирования реализовали Singleton через приватный статический объект, однако при сериализации и последующей десериализации (например, в кластерной среде) возникало несколько экземпляров. Проблема решилась добавлением readResolve().
История
В системе аналитики разработчик реализовал thread-safe Singleton через synchronized метод getInstance(). На высоконагруженной системе в момент пика появилось резкое падение производительности, выяснилось: вызовы getInstance() (тысячи раз в секунду) блокировали друг друга из-за ненужной синхронизации, хотя инициализация нужна только один раз.