Синглетон — это класс, у которого существует только один экземпляр, к которому предоставляется доступ через одну глобальную точку доступа. Важно, что есть только один экземпляр объекта, а не просто набор методов.
Плюсы и минусы синглетонов
Плюсы:
- Контролируемый доступ к единственному экземпляру (например, логгера или игрового мира).
Минусы:
- Усложняет написание тестов.
- Усложняет развитие ПО, если в дальнейшем окажется, что нужно иметь несколько экземпляров этого объекта.
Примеры реализаций синглетонов в Java
Вариант 1
Самый простой. Создание экземпляра фактически будет происходить при первом обращении к любому поли или методу класса Singleton (не обязательно к методу getInstance()). Потокобезопасен.
1 2 3 4 5 6 7 |
public class Singleton { private static Singleton instance = new Singleton(); public static Singleton getInstance() { return instance; } } |
Вариант 2
Несинхронизированный вариант. Только для однопоточных приложений.
1 2 3 4 5 6 7 8 9 10 11 |
public class Singleton { private static Singleton instance; private Singleton () {} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } |
Вариант 3
Несинхронизированный вариант с возможностью обработки исключений создания экземпляра и неленивой инициализацией. Создание экземпляра будет происходить при первом доступе к полю или методу класса Singleton, то есть для случая, когда кроме метода getInstance() в нём нет других публичных методов и полей, этот вариант вполне можно считать вариантом с отложенной инициализацией.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public class Singleton { private static Singleton instance; static { try { instance = new Singleton(); } catch (SomeException ex) { // Обработка исключения. } } private Singleton () {} public static Singleton getInstance() { return instance; } } |
Вариант 4
Использование static synchronized метода для синхронизации доступа к экземпляру и его создания. Из недостатков можно отметить, что этот способ блокирует метод getInstance() не зависимо от того, создали мы уже экземпляр или нет, что замедляет работу при использовании нескольких потоков. Подробнее про модификатор synchronized я уже описывал в статье про многопоточность в Java.
1 2 3 4 5 6 7 8 9 10 11 |
public class Singleton { private static Singleton instance; private Singleton () {} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } |
Вариант 5
Вариант с вложенным блоком synchronized и повторной проверкой существования экземпляра. Лишён недостатка предыдущего метода. Модификатор volatile для поля instance нужен, чтобы запись в переменную была видна целиком из других потоков.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class Singleton { private static volatile Singleton instance; private Singleton() {} public static Singleton getInstance() { Singleton localInstance = instance; if (localInstance == null) { synchronized (Singleton.class) { localInstance = instance; if (localInstance == null) { instance = localInstance = new Singleton(); } } } return localInstance; } } |
Вариант 6
Ленивая инициализация без необходимости синхронизации. Основная хитрость этого способа состоит в том, что при вызове Singleton.getInstance() класс SingletonHolder ещё не будет загружен и инициализирован, а значит экземпляр будет создаваться только при обращении к getInstance(). А хитрость отсутствия необходимости инициализации в том, что загрузка и инициализация класса в любом случае будет проходить ClassLoader-ом синхронизировано, другие потоки не смогут получить статическое поле из SingletonHolder, пока он не будет инициализирован. Рекомендую изучить подробнее порядок инициализации классов в Java, чтобы до конца осознать мудрость этого способа.
1 2 3 4 5 6 7 8 9 10 11 |
public class Singleton { private Singleton() {} private static class SingletonHolder { public static final Singleton instance = new Singleton(); } public static Singleton getInstance() { return SingletonHolder.instance; } } |
Вариант 7
Потокобезопасная реализация с использованием enum. Здесь существование единственного экземпляра обеспечивается самой JVM. Рекомендую узнать подробнее про перечисления в Java.
1 2 3 4 5 6 7 8 9 10 11 |
public enum SingletonEnum { INSTANCE; public void someMethod() { *** } public void anotherMethod() { *** } } |
А для чего он нужен, кроме как для того, чтобы про него на собеседованиях спрашивали?
Midnight Owl,
Синглтон это как твоя личность, которая существует во вселенной только в одном экземпляре до самой твоей смерти, другой такой больше не будет. Ты можешь её раскрыть любому, но тогда не исключена возможность что в неё могут нагадить, а можешь замкнуться в себе и не познать этот Мир!
Теперь понятнее стало? 😉