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

Модель Singleton с комбинацией ленивой загрузки и безопасности резьбы

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

Пример ожидающей инициализации:

public class Singleton
{
    //initialzed during class loading
    private static final Singleton INSTANCE = new Singleton();

    //to prevent creating another instance of Singleton
    private Singleton(){}

    public static Singleton getSingleton(){
        return INSTANCE;
    }
}

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

поэтому я придумываю такой подход:

public final class Foo {
    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }
    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }
    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }
}

Как показано выше, так как строка

private static final Foo INSTANCE = new Foo(); 

выполняется только тогда, когда фактически используется класс FooLoader, это заботится о ленивом экземпляре, и гарантируется, что он будет потокобезопасным.

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

4b9b3361

Ответ 1

Второй фрагмент кода - это, на мой взгляд, лучший способ потокобезопасной ленивой инициализации синглтона. На самом деле это имеет имя шаблона

Инициализация по требованию идиома

Я предлагаю вам использовать его.

Ответ 2

Вы первый дизайн на самом деле ленив. Подумайте об этом, экземпляр создается только при инициализации класса; класс инициализируется только тогда, когда метод getSingleton() называется [1]. Таким образом, экземпляр создается только тогда, когда он запрашивает, т.е. Лениво создается.

[1] http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.1

Ответ 3

Вторая очень плохая с точки зрения удобочитаемости, первая из них подходит. Посмотрите на статью . Его двойная проверка блокировки, но также даст вам широкую информацию о многопоточности однопользователей.

Ответ 4

Лучший способ - использовать Enum Way:

public enum Singleton {
    INSTANCE;
    public void execute (String arg) {
            //... perform operation here ...
    }
}

Ответ 5

По-моему, это неприемлемая модель использования. Он делает предположения о том, что поведение JVM является нетривиальным и запутанным. Кроме того, он имеет фиктивный класс. Следует избегать классов манекенов, если это возможно.

Я предлагаю простой подход:

public class Foo {
    private volatile static final Foo instance = null;

    private Foo() {
    }

    public static Foo instance() {
        if (instance == null) instance = new Foo();
        return instance;
        }
    }
}

... хотя, это не работает как есть - это не потокобезопасно. То, что вы действительно хотите, это шаблон двойной проверки, представленный в статье 71 блоха Эффективная Java; см. здесь. Адаптируя пример по ссылке на ваш случай, получаем:

public class Foo {
    private volatile static final Foo instance = null;

    private Foo() {
    }

    public static Foo instance() {
        if (instance != null) return instance;
        synchronized(instance) {
           Foo result = instance;
           if (instance == null) {
                result = instance = new Foo();
           return result;
        }
    }
}

Примечания:

  • Не беспокойтесь о производительности этого кода, современные JVM позаботятся об этом, и это просто отлично. В конце концов, преждевременная оптимизация - корень всего зла.
  • Как предлагается в других ответах, вышесказанное не является предпочтительным решением Блоха, но я думаю, что использование перечисления для singleton семантически неуместно, как и то, что первоначально делал OP.