шаблон "одиночка" в Java. ленивая инициализация

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

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

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

 return _instance;
} 

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

6
задан jruizaranguren 22 October 2015 в 06:58
поделиться

6 ответов

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

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

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
}

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

В 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; }
}
27
ответ дан 8 December 2019 в 02:16
поделиться

Различные подходы к синглетонам с отложенной загрузкой обсуждаются Бобом Ли в Синглтоны с отложенной загрузкой и «подход - это идиома инициализации по требованию (IODH) , которая требует очень небольшого количества кода и имеет нулевые накладные расходы на синхронизацию.

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

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

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

4
ответ дан 8 December 2019 в 02:16
поделиться

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

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;    
  }
}
0
ответ дан 8 December 2019 в 02:16
поделиться

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

if (_instance==null) {

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

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

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

4
ответ дан 8 December 2019 в 02:16
поделиться

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

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

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

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

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

5
ответ дан 8 December 2019 в 02:16
поделиться

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

2
ответ дан 8 December 2019 в 02:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: