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

Различные способы написания singleton в Java

Классик написания singleton в java выглядит так:

public class SingletonObject
{
    private SingletonObject()
    {
    }

    public static SingletonObject getSingletonObject()
    {
      if (ref == null)
          // it ok, we can call this constructor
          ref = new SingletonObject();
      return ref;
    }

    private static SingletonObject ref;
}

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

Но я предпочитаю писать его как:

public class SingletonObject
{
    private SingletonObject()
    {
        // no code req'd
    }

    public static SingletonObject getSingletonObject()
    {
      return ref;
    }

    private static SingletonObject ref = new SingletonObject();
}

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

4b9b3361

Ответ 1

Разница между вашим кодом и "образцом кода" заключается в том, что ваш singleton создается при загрузке класса, а в "образцовой" версии он не создается, пока он не понадобится.

Ответ 2

Во второй форме ваш синглтон загружен с нетерпением, и на самом деле это предпочтительная форма (и первая из них не является потокобезопасной, поскольку вы упомянули ее сами). Желательная загрузка - это не плохо для производственного кода, но есть контексты, где вам может понадобиться ленивая загрузка ваших синглетов, о чем говорил автор Guice, Боб Ли, в Lazy Loading Singletons, которые я цитирую ниже:

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

До Java 1.5 я ленивый загружался синглетоны, использующие простой старый синхронизация, простая, но эффективная:

static Singleton instance;

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

Изменения в модели памяти в 1.5 включил печально известный Double-Checked Блокировка (DCL). Чтобы реализовать DCL, вы проверяете поле volatile в общий путь и только синхронизировать, когда необходимо:

static volatile Singleton instance;

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

Но volatile не намного быстрее чем synchronized, synchronizedдовольно быстро в настоящее время, и DCL требует больше кода, поэтому даже после того, как вышел 1.5, Я продолжал использовать простой старый синхронизации.

Представьте мое удивление сегодня, когда Джереми Мэнсон указал мне на Инициализация по требованию владельца (IODH), который требует очень маленький код и имеет нулевой накладные расходы синхронизации. Нуль, как в даже быстрее, чем volatile. IODH требует того же количества строк как обычная старая синхронизация, и это быстрее, чем DCL!

IODH использует ленивый класс инициализация. JVM не будет выполнять статический инициализатор класса до тех пор, пока вы на самом деле касаться чего-то в классе. Это относится к статическим вложенным классам, слишком. В следующем примере JLS гарантирует JVM не будет инициализировать instance, пока кто-то вызывает getInstance():

static class SingletonHolder {
  static Singleton instance = new Singleton();    
}

public static Singleton getInstance() {
  return SingletonHolder.instance;
}

[...]

Обновление: Кредит, при котором должен быть кредит, Эффективная Java (авторское право 2001) подробно описывает этот шаблон по пункту 48. Далее указывается, что вам все равно придется использовать синхронизацию или DCL в нестатических контекстах.

Я также переключил однотоновую обработку в моя структура от синхронизации до DCL и увидел еще 10% производительности (по сравнению с тем, как я начал используя быстрое отображение cglib). только я использовал один поток в моем микро-контроле, поэтому повышение до concurrency может быть еще больше, учитывая, что я заменил сильно заблокированный замок с относительно мелкозернистая летучее поле доступ.

Обратите внимание, что Джошуа Блох теперь рекомендует (начиная с Effective Java, 2nd ed) реализовать синглтоны, используя один элемент enum, как указано Jonik.

Ответ 3

Ну, в последнем случае объект singleton создается до того, как он когда-либо понадобится, но в большинстве случаев это, вероятно, не ужасно плохо.

Кстати, Джошуа Блох рекомендует (в "Эффективной Java", 2-е изд., п. 3), реализуя синглтоны, используя одноэлементное перечисление:

public enum SingletonObject { 
    INSTANCE;
}

Он дает следующее обоснование:

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

Ответ 4

Я бы сказал, что последний код является более стандартным. Ваша первая версия не является потокобезопасной. Способы обеспечения безопасности потока включают в себя синхронизацию при каждом доступе или очень тщательное использование блокировки с двойным проверкой (что безопасно с моделью памяти Java 5, если вы это исправите).

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

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

В "Эффективной Java" есть более подробная информация об этом, но я не имею его со мной, чтобы найти номер элемента, я боюсь.

Ответ 5

Я думаю, ваша проблема в том, что вы смешиваете одиночную и ленивую инициализацию. Один синглтон может быть реализован с помощью различных стратегий инициализации:

  • инициализация загрузки классов
  • ленивая инициализация, использующая двойную блокировку
  • ленивая инициализация с единственной проверкой (с возможной повторной инициализацией)
  • ленивая инициализация, использующая загрузчик классов (идентификатор класса владельца)

Все эти подходы обсуждаются в Эффективная Java 2nd Item 71: разумно используйте ленивую инициализацию.

Ответ 6

Различные способы реализации singleton pattern в java выглядит следующим образом

public class SingletonClass {
private SingletonClass= null;    
public static SingletonClass getInstance() {
    if(SingletonClass != null) {
       SingletonClass = new SingletonClass();
    }
  } 
}

Это не потокобезопасно. Ниже приведена thread safe реализация шаблона одноэлементного дизайна

1. Драконовская синхронизация

private static YourObject instance;

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

2. Синхронизация двойной проверки

Ссылка

private static final Object lock = new Object();
private static volatile YourObject instance;

public static YourObject getInstance() {
    YourObject r = instance;
    if (r == null) {
        synchronized (lock) {    // While we were waiting for the lock, another 
            r = instance;        // thread may have instantiated the object.
            if (r == null) {  
                r = new YourObject();
                instance = r;
            }
        }
    }
    return r;
}

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

Ссылка

public class Something {
    private Something() {}

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

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

4. Другой использует enum

public enum Singleton {
    SINGLE;
    public void myMethod(){  
    }
}

Ответ 7

второй способ разрешит проблему многопоточности, но всегда будет создавать Singleton, даже если вам это не понадобится. Лучший способ сделать это - создать класс Nested.

public class singleton 
{
    private singleton() 
    {
       System.out.println("I'am called only when it needed");
    }

    static class Nested 
    {
       Nested() {}
       private static final singleton instance = new singleton();
    }

    public static singleton getInstance() 
    {
       return Nested.instance;
    }

    public static void main(String [] args) 
    {
      singleton.getInstance();
    }
}

Ответ 8

Ниже приведен лучший способ написать одноэлементный класс. Попробуйте его

public final class SingeltonTest {
    /**
     * @param args
     * @return
     */
    private static SingeltonTest instance = null;

    private SingeltonTest() {
        System.out.println("Rahul Tripathi");
    }

    public static SingeltonTest getInstance() {
        if (instance == null) {
            synchronized (SingeltonTest.class) {
                if (instance == null)
                    instance == new SingeltonTest();
            }
        }
        return instance;
    }
}

Ответ 9

// Lazy loading enabled as well as thread safe

class Singleton {

                private static class SingletonHolder {
                public static Singleton instance = new Singleton();
                }

                public static Singleton getInstance() {
                   return SingletonHolder.instance;
                  }
               }

Ответ 10

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

public class SingletonObject
{
public static SingletonObject REF = new SingletonObject();

private SingletonObject()
{
    // no code req'd
}

}