Подтвердить что ты не робот

Thread Safe Эффективный способ реализации одноэлементного шаблона в Java?

Возможный дубликат:
Эффективный способ реализации одноэлементного шаблона в Java

Я читал эту Лучшую реализацию Singleton в Java, но ее безопасность не была потоковой.

Согласно wiki:

if(singleton==null) { synchronized(Singleton.class) { // this is needed if two threads are waiting at the monitor at the // time when singleton was getting instantiated if(singleton==null) singleton= new Singleton(); }
}

Но утилита Find Bugs дает две ошибки: 1. Двойная проверка нуля. 2. Неправильная ленивая инициализация статического поля.

Каков наилучший способ,

Правильно ли это:

synchronized (Singleton.class) {
if (singleton== null) {
singleton= new Singleton();
}
}
4b9b3361

Ответ 1

Самый эффективный/простой способ сделать ленивую загрузку Singleton - это просто

enum Singleton {
   INSTANCE
}

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

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

ИМХО, вы должны быть довольно параноидальными, чтобы считать это лучшим решением.

Ответ 2

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

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

private static MyClass instance = new MyClass();

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

public class MyClass {
    private static class MyClassInit {
        public static final MyClass instance = new MyClass();
    }

    public static MyClass getInstance() {
        return MyClassInit.instance; 
    }
...
}

Вложенный класс не будет загружен до первого вызова getInstance().

Ответ 3

Первый пример кода в принятом ответе для Эффективный способ реализации одноэлементного шаблона в Java является потокобезопасным. Создание INSTANCE выполняется загрузчиком класса при первом загрузке класса; он выполняется ровно один раз и в потокобезопасном режиме:

public final class Foo {

    private static final Foo INSTANCE = new Foo();

    private Foo() {
        if (INSTANCE != null) {
                throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return INSTANCE;
    }
}

(скопирован из Что представляет собой эффективный способ реализации одноэлементного шаблона в Java?)

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