Ответ 1
Второй фрагмент кода - это, на мой взгляд, лучший способ потокобезопасной ленивой инициализации синглтона. На самом деле это имеет имя шаблона
Инициализация по требованию идиома
Я предлагаю вам использовать его.
Я занимался некоторыми исследованиями о синглетонах, в частности, относительно ленивой и нетерпеливой инициализации одиночных особей.
Пример ожидающей инициализации:
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, это заботится о ленивом экземпляре, и гарантируется, что он будет потокобезопасным.
Правильно ли это?
Второй фрагмент кода - это, на мой взгляд, лучший способ потокобезопасной ленивой инициализации синглтона. На самом деле это имеет имя шаблона
Инициализация по требованию идиома
Я предлагаю вам использовать его.
Вы первый дизайн на самом деле ленив. Подумайте об этом, экземпляр создается только при инициализации класса; класс инициализируется только тогда, когда метод getSingleton()
называется [1]. Таким образом, экземпляр создается только тогда, когда он запрашивает, т.е. Лениво создается.
[1] http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.1
Вторая очень плохая с точки зрения удобочитаемости, первая из них подходит. Посмотрите на статью . Его двойная проверка блокировки, но также даст вам широкую информацию о многопоточности однопользователей.
Лучший способ - использовать Enum Way:
public enum Singleton {
INSTANCE;
public void execute (String arg) {
//... perform operation here ...
}
}
По-моему, это неприемлемая модель использования. Он делает предположения о том, что поведение 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;
}
}
}
Примечания: