одноэлементный шаблон на java. ленивая инициализация

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, но вопрос, который я опубликовал, заключается в том, чтобы знать в основном разницу "синхронизировать" на уровне метода и блока в этом конкретном случае.

6 ответов


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

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

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

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

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

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


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

if (_instance==null) {

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

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

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


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

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

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

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


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


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

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