Singleton pattern в java. ленивая инициализация

89
13

public static MySingleton getInstance() {
if (_instance==null) {
synchronized (MySingleton.class) {
_instance = new MySingleton();
}
}
return _instance;
}

1. Есть ли недостаток с вышеупомянутой реализацией метода getInstance?
2. В чем разница между двумя реализациями?


public static synchronized MySingleton getInstance() { 
if (_instance==null) {
_instance = new MySingleton();
}

return _instance;
}


Я видел много ответов на одноэлементный шаблон в stackoverflow, но вопрос, который я опубликовал, заключается в том, чтобы знать в основном отличие "синхронизации" на методе и уровне блока в этом конкретном случае.

спросил(а) 2010-03-26T11:29:00+03:00 9 лет, 8 месяцев назад
6
Решение
168

1. Есть недостаток с вышеупомянутой реализацией getInstance метод?



Это не работает. Вы можете получить несколько экземпляров вашего Singleton.


2. В чем разница между двумя реализациями?



Второй работает, но требует синхронизации, что может замедлить работу системы, когда у вас много доступа к методу из разных потоков.


Самая простая правильная реализация:


public class MySingleton{
private static final MySingleton _instance = new MySingleton();
private MySingleton(){}
public static MySingleton getInstance() {
return _instance;
}
}

Короче и лучше (безопасно сериализуемое):


public enum MySingleton{
INSTANCE;

// methods go here
}


Ленивая инициализация синглетонов - это тема, которая не учитывает значительную практическую полезность (ИМО, споря о тонкостях двойной проверки блокировки, к которой ваш пример является первым шагом, это не что иное, как мошенничество).


В 99% всех случаев вам вообще не нужна ленивая инициализация, или "init когда класс сначала называется" Java достаточно хорош. В оставшихся 1% случаев это лучшее решение:


public enum MySingleton{
private MySingleton(){}
private static class Holder {
static final MySingleton instance = new MySingleton();
}
static MySingleton getInstance() { return Holder.instance; }
}

ответил(а) 2010-03-26T11:37:00+03:00 9 лет, 8 месяцев назад
Еще 5 ответов
77

1. Есть недостаток с вышеупомянутой реализацией getInstance метод?



Да, синхронизированное ключевое слово должно также включить оператор if. Если это не так, то два или более потока могут потенциально проникнуть в код создания.


2. В чем разница между двумя реализациями?


Вторая реализация верна и с моей точки зрения легче понять.


Использование синхронизации на уровне метода для статического метода синхронизируется с классом, т.е. то, что вы сделали в примере 1. Использование синхронизации на уровне метода для метода экземпляра синхронизируется с экземпляром объекта.

ответил(а) 2010-03-26T11:35:00+03:00 9 лет, 8 месяцев назад
71

Различные подходы к ленивым синглонам обсуждаются Боб Ли в
Lazy Loading Singletons и "правильный" подход - Инициализация по требованию Holder (IODH), который требует очень мало кода и имеет нулевые служебные данные синхронизации.


static class SingletonHolder {
static Singleton instance = new Singleton();
}

public static Singleton getInstance() {
return SingletonHolder.instance;
}


Боб Ли также объясняет, когда он хочет ленить загрузку сингла (во время тестов и разработки). Честно говоря, я не уверен, что есть огромная выгода.

ответил(а) 2010-03-26T12:48:00+03:00 9 лет, 8 месяцев назад
73

Первый из них ошибочен двумя способами. Как упоминалось выше, несколько потоков могут пройти через


if (_instance==null) {

Они будут ждать друг друга, пока объект не будет полностью сконструирован, но они будут создавать экземпляр и заменить ссылку в переменной.


Второй недостаток немного сложнее. Один поток может попасть в конструктор new MySingleton(), а затем JVM переключается на другой поток. Другой поток может проверить переменную для null, но может содержать ссылку на частично построенный объект. Так что другая нить работает на частично построенном синглтоне, что тоже не очень хорошо. Поэтому следует избегать первого варианта.


Второй вариант должен работать нормально. Не беспокойтесь о эффективности, пока вы не определите это как блокирующий. Современные JVM могут оптимизировать ненужные синхронизации, поэтому в реальном производственном коде эта конструкция никогда не может повредить производительность.

ответил(а) 2010-03-26T11:49:00+03:00 9 лет, 8 месяцев назад
53

Второй - поточно-безопасный, но он имеет накладные расходы на синхронизацию при каждом вызове, независимо от того, сконструирован ли экземпляр или нет. Первый вариант имеет один главный недостаток: у него нет проверки на наличие (_instance == null) в синхронизированном блоке, чтобы предотвратить создание двух экземпляров.

ответил(а) 2010-03-26T11:38:00+03:00 9 лет, 8 месяцев назад
32

Я бы предложил следующую реализацию


public class MySingleTon
{

private static MySingleton obj;

//private Constructor
private MySingleTon()
{
}

public static MySingleTon getInstance()
{
if(obj==null)
{
synchronized(MySingleTon.class)
{
if(obj == null)
{
obj = new MySingleTon();
}
}
}
return obj;
}
}

ответил(а) 2010-03-26T13:53:00+03:00 9 лет, 8 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема