Резьба безопасности в Синглтоне - PullRequest
13 голосов
/ 26 мая 2010

Я понимаю, что двойная блокировка в Java не работает, так как лучше всего сделать Singletons Thread Safe в Java? Первое, что приходит мне в голову:

class Singleton{
    private static Singleton instance;

    private Singleton(){}

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

Это работает? если это так, то это лучший способ (я думаю, это зависит от обстоятельств, поэтому было бы полезно указать, когда конкретный метод является лучшим)

Ответы [ 5 ]

25 голосов
/ 26 мая 2010

Джош Блох рекомендует использовать одноэлементный тип enum для реализации синглетонов (см. Effective Java 2nd Edition, пункт 3: принудительное использование свойства singleton с помощью частного конструктора или типа enum ).

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

Следующий пример взят прямо из книги.

public enum Elvis {
   INSTANCE;

   public void leaveTheBuilding() { ... }
}

Вот его заключительные аргументы:

Этот подход [...] более лаконичен, предоставляет механизм сериализации бесплатно и обеспечивает железную гарантию от множественных экземпляров, даже перед сложными атаками сериализации или отражения. Хотя этот подход еще не получил широкого распространения, одноэлементный тип перечисления является лучшим способом реализации синглтона .


На enum постоянная единичная гарантия

JLS 8,9. Перечни

Тип enum не имеет экземпляров, отличных от определенных его константами. Попытка явно создать экземпляр типа enum - это ошибка времени компиляции (§15.9.1).

Метод final clone в Enum гарантирует, что константы перечисления никогда не могут быть клонированы, а специальная обработка механизмом сериализации гарантирует, что повторяющиеся экземпляры никогда не будут созданы в результате десериализации. Рефлексивная реализация типов перечислений запрещена. Вместе эти четыре вещи гарантируют, что экземпляров типа enum не существует, кроме тех, которые определены константами enum.


При отложенной инициализации

Следующий фрагмент:

public class LazyElvis {
    enum Elvis {
        THE_ONE;
        Elvis() {
            System.out.println("I'M STILL ALIVE!!!");
        }       
    }
    public static void main(String[] args) {
        System.out.println("La-dee-daaa...");
        System.out.println(Elvis.THE_ONE);
    }
}

Создает следующий вывод:

La-dee-daaa...
I'M STILL ALIVE!!!
THE_ONE

Как видите, константа THE_ONE не создается в конструкторе до тех пор, пока к ней не обращаются в первый раз.

8 голосов
/ 26 мая 2010

Я не вижу проблем с вашей реализацией (кроме того факта, что блокировка одноэлементного монитора может использоваться другими методами по другим причинам и, таким образом, излишне мешать другим потокам получить экземпляр). Этого можно избежать, введя дополнительный Object lock для блокировки.

Эта статья в Википедии предлагает другой метод:

public class Something {
    private Something() {
    }

    private static class LazyHolder {
        private static final Something INSTANCE = new Something();
    }

    public static Something getInstance() {
        return LazyHolder.INSTANCE;
    }
}

Из статьи:

Эта реализация является хорошо работающей и параллельной реализацией, действительной во всех версиях Java.
...
Реализация опирается на четко определенную фазу инициализации выполнения в виртуальной машине Java (JVM); подробности см. в разделе 12.4 Спецификации языка Java (JLS).

5 голосов
/ 26 мая 2010

Я предпочитаю просто сделать:

class Singleton {
    private static final INSTANCE = new Singleton();

    private Singleton(){}

    public Singleton instance(){
        return INSTANCE;
    }
 }

Редко требуется ленивая инициализация. Вы всегда должны начинать с активной инициализации и переходить на ленивую инициализацию только в случае возникновения проблем. Если вы не измерили и не определили экземпляр Singleton как виновника проблемы производительности, просто используйте готовую инициализацию. Это проще и эффективнее.

Вы можете наверняка использовать enum, но лично я не беспокоюсь, потому что преимущество по сравнению с обычным нетерпеливым созданием экземпляров - это безопасность (против атаки отражением), и в большинстве случаев мой код в любом случае уязвим для таких атак:

2 голосов
/ 26 мая 2010

Вы можете использовать этот фрагмент кода из вики

public class Singleton {
   // Private constructor prevents instantiation from other classes
   private Singleton() {}

   /**
    * SingletonHolder is loaded on the first execution of Singleton.getInstance() 
    * or the first access to SingletonHolder.INSTANCE, not before.
    */
   private static class SingletonHolder { 
     private static final Singleton INSTANCE = new Singleton();
   }

   public static Singleton getInstance() {
     return SingletonHolder.INSTANCE;
   }
 }
2 голосов
/ 26 мая 2010

Джош Блох рекомендует 2 решения:

1) worser:

class Singleton {

   public static Singleton instance = new Singleton();

   ...
}

2) лучше:

public enum Singleton {

   INSTANCE;

   ...
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...