Есть ли проблемы в многопоточной среде с шаблоном Singleton?

Я реализовал класс Singleton, как показано ниже:

public class Singleton {

    private static  Singleton instance = null;


    private Singleton() { 
    }

private synchronized static void createInstance() {
    instance = new Singletone();
}


    public static Singleton getInstance() {
        if(instance == null){
            createInstance();
        }
        return instance;
    }

}

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


person Sharad Ahire    schedule 30.11.2011    source источник
comment
Опечатка: это Singleton, а не Singletone. И да, это не потокобезопасный синглтон.   -  person zengr    schedule 30.11.2011
comment
возможный дубликат безопасного потока java singleton   -  person zengr    schedule 30.11.2011
comment
Возможный дубликат: stackoverflow .com/questions/70689/   -  person Alex K    schedule 30.11.2011


Ответы (7)


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

public static synchronized Singletone getInstance() {
    if(instance == null){
        createInstance();
    }
    return instance;
} 

Обратите внимание на слово synchronized.

person AlexR    schedule 30.11.2011

public enum Singleton {
    INSTANCE;
    private int val;

    public int getVal() {
        return val;
    }
}

Использование:

Singleton.INSTANCE.getVal();

Это идеальный синглтон для версий Java > 5.0, где у вас есть поддержка enum.

Также упоминается в «Эффективной Java» Джошуа Блоха. Сообщение в блоге об этом здесь: Enum Singleton

Обновление:
Кроме того, используйте синглтоны только тогда, когда вы на 100 % уверены, что они вам нужны! Это убивает тестируемость кода! Но вы не можете избежать его в некоторых местах, скажем, на Фабрике.
Но, пожалуйста, не злоупотребляйте им, используйте его там, где он вам действительно нужен. Поймите его использование.

person zengr    schedule 30.11.2011
comment
Извините, случайно проголосовал против, и оказалось, что я не могу это очистить, поэтому +1 - следующая лучшая вещь. - person Steve Rukuts; 30.11.2011

Лучший механизм, с которым я столкнулся, кроме перечисления выше, называется статической инициализацией. При этом вы полагаетесь на гарантии модели памяти Java, поэтому она всегда работает. Вот фрагмент из ответа на другой вопрос, который демонстрирует это:

class Singleton {
   static class SingletonHolder {
      static final Singleton INSTANCE = new Singleton();
   }
   public static Singleton instance() {
      return SingletonHolder.INSTANCE;
   }
}

Объект класса SingletonHolder с экземпляром Singleton будет создан при первом вызове SingletonHolder.INSTANCE.

Модель памяти Java гарантирует, что статический код (new Singleton()) будет выполняться только одним потоком. Так что никакой блокировки с двойной проверкой (которая не работает) и ненужной синхронизации. Все последующие вызовы будут получать этот экземпляр.

person Jakub Korab    schedule 30.11.2011

Это правильная реализация одноэлементного шаблона, хотя на самом деле вам не нужен метод createInstance; вы можете просто встроить это в getInstance. Кроме того, оно пишется «Singleton», без буквы «e» в конце.

Гипотетически вы можете создать проблему в многопоточной среде. Если два указателя фрейма входят в getInstance одновременно, тот, который вошел первым, может получить экземпляр Singleton, а второй — другой экземпляр.

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

person Steve Rukuts    schedule 30.11.2011

Для реализации ленивой инициализации, как в вашем случае, метод getInstance() должен быть синхронизирован для обеспечения безопасности потоков.

public static synchronized Singleton getInstance()

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

private static  Singleton instance = new MySingleton(); 
person bsrykt    schedule 30.11.2011

Синглтон не является потокобезопасным. Именно по этой причине была введена блокировка с двойной проверкой.

Начиная с Java SE 5 и выше, вы можете volatile свой статический экземпляр. Виртуальная машина Java будет знать, как правильно обрабатывать Singleton при выполнении нескольких потоков.

Подробнее об блокировке с двойной проверкой.

person Buhake Sindi    schedule 30.11.2011
comment
Из ссылки, которую вы включили: у этой техники много тонких проблем, и ее обычно следует избегать. - person Jakub Korab; 30.11.2011

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

См. http://java.sun.com/developer/technicalArticles/Programming/singletons/ для краткого изложения этих проблем.

О, и это синглтон, а не синглтон. :)

person vertti    schedule 30.11.2011
comment
На самом деле это возможная* проблема многопоточности даже в одиночных JVM. Ваша ссылка также охватывает это в разделе «Несколько экземпляров в результате неправильной синхронизации». - person Boris; 30.11.2011