шаблон синглтона в Java. ленивая инициализация - PullRequest
6 голосов
/ 26 марта 2010
public static MySingleton getInstance() {
 if (_instance==null) {
   synchronized (MySingleton.class) {
      _instance = new MySingleton();
   }
 }
 return _instance;
} 

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

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

 return _instance;
} 

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

Ответы [ 6 ]

27 голосов
/ 26 марта 2010

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

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

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

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

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

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; }
}
5 голосов
/ 26 марта 2010

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

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

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

Вторая реализация верна и, на мой взгляд, более понятна.

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

4 голосов
/ 26 марта 2010

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

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

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

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

4 голосов
/ 26 марта 2010

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

if (_instance==null) {

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

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

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

2 голосов
/ 26 марта 2010

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

0 голосов
/ 26 марта 2010

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

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;    
  }
}
...