Вы можете избежать некоторой путаницы, устраняя риск lazy initialization (за который вы в настоящее время платите за исключение).Поскольку вы действительно просто возвращаете свой статический экземпляр, почему бы просто не создать этот статический экземпляр во время выполнения (т. Е. Не ждать нулевое значение):
class MySingleton {
private static MySingleton instance = new MySingleton();
// You could do this here or in the constructor
// private OtherObject obj = new OtherObject();
/** Use this if you want to do it in the constructor instead. */
private OtherObject obj;
private MySingleton() {
obj = new OtherObject();
}
/** Now you can just return your static reference */
public static MySingleton getInstance() {
return instance;
}
}
Если вы заметили, теперь все детерминировано ипростой.Ваш MySingleton.instance заполняется во время выполнения и доступен статическим методом MySingleton.getInstance()
.
. Я понимаю, что это не соответствует точному шаблону исходной книги по шаблонам проектирования GOF, но вы заметите, чтоиспользование класса практически одинаково.
РЕДАКТИРОВАТЬ : продолжая некоторые вопросы безопасности потоков, поднятые в других ответах и комментариях, я попытаюсь проиллюстрировать, как оригиналПредлагаемое решение в вопросе и в книге GOF не является потокобезопасным.Для получения более подробной информации см. Параллелизм Java на практике , который у меня есть и есть на моей книжной полке ACM / Safari.Это, откровенно говоря, лучший источник, чем мой набросок, но, эй, мы можем только стремиться ...
Итак, представьте, что у нас есть два потока с именами Thread1 и Thread2, и, по совпадению, каждый из них обращается к MySingleton.getInstance () метод в тот же момент.Это вполне возможно в современной многоядерной гиперпоточной системе.Теперь, хотя эти два потока случайно достигли точки входа этого метода в одно и то же время, нет уверенности в том, что произойдет какое-либо конкретное утверждение.Итак, вы можете увидеть что-то вроде этого:
// Note that instance == null as we've never called the lazy getInstance() before.
Thread1: if (instance == null)
Thread2: if (instance == null) // both tests pass - DANGER
Thread1: instance = new MySingleton();
Thread2: instance = new MySingleton(); // Note - you just called the constructor twice
Thread1: return instance; // Two singletons were actually created
Thread2: return instance; // Any side-effects in the constructor were called twice
Проблема, проиллюстрированная в тесте if-null, называется условие гонки .Вы не можете знать, кто и на какое заявление пойдет.В результате, как будто оба потока гоняют друг друга к обрыву.
Если вы посмотрите на тот же анализ моего примера, когда оба потока все еще нажимают на getInstance () в одно и то же время, вы видите что-товот так:
Thread1: return instance;
Thread2: return instance;
Упрощенно, да, но это моя точка зрения.Объект был создан один раз, давным-давно, и потоки могут рассчитывать на его согласованность значений.Расы не существует.
ПРИМЕЧАНИЕ: вам все равно придется беспокоиться о содержимом конструктора для OtherObject.Урок: параллелизм труден.Если вы делаете свой конструктор потоком безопасным (что и нужно), убедитесь, что автор OtherObject проявил к вам ту же любезность.