Singleton класс в java

Просто я думал о других способах написания класса Singleton. Итак, этот класс рассматривается как одноэлементный класс?

      public class MyClass{
            static Myclass myclass;

            static { myclass = new MyClass();}

            private MyClass(){}

            public static MyClass getInstance()
            { 
                return myclass;
            }
       }

поскольку статический блок выполняется только один раз.

Ответ 1

Нет, это не так. Вы не объявили myClass private static final, а getInstance() - static. Код также не компилируется.

Здесь Singleton idiom:

public class MyClass {
    private static final MyClass myClass = new MyClass();

    private MyClass() {}

    public static MyClass getInstance() {
        return myClass; 
    }
}

Он должен быть private, так что никто другой не может получить к нему доступ напрямую. Это должно быть static, чтобы было только одно из них. Он должен быть final, чтобы его нельзя было переназначить. Вам также необходимо создать экземпляр непосредственно во время объявления, чтобы вам не нужно было беспокоиться о потоке.

Если загрузка дорогая, и поэтому вы предпочитаете ленивую загрузку Singleton, тогда рассмотрите Singleton holder, который делает инициализацию по требованию вместо во время загрузки класса:

public class MyClass {
    private MyClass() {}

    private static class LazyHolder {
        private static final MyClass myClass = new MyClass();
    }

    public static MyClass getInstance() {
        return LazyHolder.myClass;
    }
}

Однако вы должны поставить большие вопросительные знаки, нужен ли вам синглтон или нет. Часто это не нужно. Лучше всего выбирать статическую переменную, перечисление, класс factory и/или инъекцию зависимостей.

Ответ 2

Вот еще один способ сделать это:

public enum Singleton{
  INSTANCE("xyz", 123);

  // Attributes
  private String str;
  private int i;

  // Constructor
  Singleton(String str, int i){
    this.str = str;
    this.i = i;
  }
}

Согласно Джошу Блоху Эффективная Java, это лучший способ реализовать Singleton в Java. В отличие от реализаций, которые связаны с частным статическим полем экземпляра, которое может быть размножено через злоупотребление отражением и/или сериализацией, перечисление гарантированно будет одиночным.

Основное ограничение с помощью перечислений enum заключается в том, что они всегда создаются в момент загрузки класса и не могут быть лениво инстанцированы. Поэтому, если вы хотите, например, создать экземпляр singleton с использованием аргументов во время выполнения, вам придется использовать другую реализацию (предпочтительно используя блокировку с двойным проверкой).

Ответ 3

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

public class MyClass
{
    private static MyClass INSTANCE=null;

    private MyClass()
    {
    }

    public static MyClass getInstance()
    {
        if(INSTANCE==null)
        {
            synchronized(MyClass.class)
            {
                if(INSATCNE==null) INSTANCE=new MyClass();
            }
        }
        return INSTANCE;
    }
}

Ответ 4

Есть 3 способа создать синглтон в Java.

  1. нетерпеливая инициализация синглтона

    public class Test {
        private static final Test TEST = new Test();
    
        private Test() {
        }
    
        public static Test getTest() {
            return TEST;
        }
    }
    
  2. ленивая инициализация синглтона (поточно-ориентированная)

    public class Test {
        private static volatile Test test;
        private Test(){}
        public static Test getTest() {
            if(test == null) {
                synchronized(Test.class) {
                    if(test == null){test = new Test();}
                }
            }
            return test;
        }
    }
    
  3. Билл Пью Синглтон с рисунком держателя (желательно лучший)

    public class Test {
        private Test(){}
    
        private static class TestHolder {
            private static final Test TEST = new Test();
        }
    
        public static Test getInstance() {
            return TestHolder.TEST;
        }
    }
    

Ответ 5

Используя ваш пример и используя способ GoF для его реализации:

public class MyClass{
    private static Myclass instance;

    private MyClass(){
        //Private instantiation
    }

    public static synchronized MyClass getInstance()  //If you want your method thread safe...
    { 
        if (instance == null) {
            instance = new MyClass();
        }

        return instance;
    }
}

Надеюсь, что это поможет:

Ответ 6

Ваш класс (исходный код, перед редактированием):

public class MyClass {
    Myclass myclass;

    static { myclass = new MyClass();}

    private MyClass(){}

    public MyClass getInstance()
    {
        return myclass;
    }
}

не является реальным синглом:

  1. поле myclass не является частным, его можно читать и изменять извне (если у вас есть возможность сделать это на
  2. поле myclass не является статическим, не может быть доступно в статическом конструкторе (ошибка компиляции)
  3. метод getInstance() не является статичным, поэтому вам нужен экземпляр для его вызова


Фактический код:

public class MyClass {
    static Myclass myclass;

    static { myclass = new MyClass();}

    private MyClass(){}

    public static MyClass getInstance()
    {
        return myclass;
    }
}

все еще имеет myclass не является частным (и не окончательным)... объявив его окончательным, поможет предотвратить его непреднамеренное изменение изнутри класса.

private static final Myclass myclass;

Ответ 7

public class singletonPattern {
    private static singletonPattern obj;

    public static singletonPattern getObject() {
        return obj = (obj == null) ? new singletonPattern() : obj;
    }

    public static void main(String args[]) {
        singletonPattern sng = singletonPattern.getObject();
    }
}

Ответ 8

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

public class MySingleton {

     private static MySingleton INSTANCE;

     public static MySingleton getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new MySingleton();
        }

        return INSTANCE;
    }
    ...
}

Здесь у нас есть класс MySingleton, который имеет частный статический член под названием INSTANCE и общедоступный статический метод, называемый getInstance(). При первом вызове getInstance() член INSTANCE равен NULL. Затем поток попадет в условие создания и создаст новый экземпляр класса MySingleton. Последующие вызовы getInstance() обнаруживают, что переменная INSTANCE уже установлена ​​и поэтому не создает другой экземпляр MySingleton. Это гарантирует, что есть только один экземпляр MySingleton, который разделяется между всеми вызывающими лицами getInstance().

Но эта реализация имеет проблему. Многопоточные приложения будут иметь условие гонки при создании единственного экземпляра. Если несколько потоков выполнения попадают в метод getInstance() в (или около) в одно и то же время, каждый из них будет видеть член INSTANCE как null. Это приведет к тому, что каждый поток создаст новый экземпляр MySingleton и впоследствии установит член INSTANCE.


private static MySingleton INSTANCE;

public static synchronized MySingleton getInstance() {
    if (INSTANCE == null) {
        INSTANCE = new MySingleton();
    }

    return INSTANCE;
}

Здесь мы использовали синхронизированное ключевое слово в сигнатуре метода для синхронизации метода getInstance(). Это, безусловно, устранит наше состояние гонки. Теперь потоки будут блокироваться и вводить метод по одному. Но это также создает проблему с производительностью. Эта реализация не только синхронизирует создание одного экземпляра, но и синхронизирует все вызовы с getInstance(), включая чтение. Чтение не нужно синхронизировать, так как они просто возвращают значение INSTANCE. Поскольку чтение будет составлять основную часть наших вызовов (помните, что создание экземпляра происходит только при первом вызове), мы будем нести лишнюю производительность, синхронизируя весь метод.


private static MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronize(MySingleton.class) {
            INSTANCE = new MySingleton();
        }
    }

    return INSTANCE;
}

Здесь мы перенесли синхронизацию из сигнатуры метода в синхронизированный блок, который завершает создание экземпляра MySingleton. Но это решает нашу проблему? Ну, мы больше не блокируем чтения, но мы также сделали шаг назад. Несколько потоков будут попадать в метод getInstance() одновременно или примерно одинаково, и все они будут видеть член INSTANCE как null. Затем они попадут в синхронизированный блок, где вы получите блокировку и создадите экземпляр. Когда этот поток выходит из блока, другие потоки будут бороться за блокировку, и по одному каждый поток будет проходить через блок и создать новый экземпляр нашего класса. Итак, мы вернулись туда, где начали.


private static MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronized(MySingleton.class) {
            if (INSTANCE == null) {
                INSTANCE = createInstance();
            }
        }
    }

    return INSTANCE;
}

Здесь мы делаем еще одну проверку из INSIDE блока. Если элемент INSTANCE уже установлен, пропустите инициализацию. Это называется двойной проверкой блокировки.

Это решает проблему множественного экземпляра. Но еще раз наше решение представляет собой еще одну проблему. Другие потоки могут не "видеть", что элемент INSTANCE обновлен. Это связано с тем, как Java оптимизирует операции с памятью. Темы копируют исходные значения переменных из основной памяти в кеш ЦПУ. Затем изменения в значениях записываются в этот кеш и считываются с него. Это особенность Java, предназначенная для оптимизации производительности. Но это создает проблему для нашей реализации singleton. Второй поток, обрабатываемый другим процессором или ядром, используя другой кеш, не увидит изменений, сделанных первым. Это приведет к тому, что второй поток увидит член INSTANCE как null, заставляющий создать новый экземпляр нашего синглтона.


private static volatile MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronized(MySingleton.class) {
            if (INSTANCE == null) {
                INSTANCE = createInstance();
            }
        }
    }

    return INSTANCE;
}

Мы решаем это, используя ключевое слово volatile при объявлении члена INSTANCE. Это позволит компилятору всегда читать и записывать в основную память, а не в кеш процессора.

Но это простое изменение происходит за счет стоимости. Поскольку мы обходим кеш-память процессора, мы будем использовать производительность каждый раз, когда будем работать с изменчивым элементом INSTANCE, который мы делаем 4 раза. Мы дважды проверяем существование (1 и 2), устанавливаем значение (3), а затем возвращаем значение (4). Можно утверждать, что этот путь является бахромой, поскольку мы создаем экземпляр только во время первого вызова метода. Возможно, успех в творчестве терпимо. Но даже наш основной прецедент, читает, будет работать на летучем члене дважды. Один раз проверить существование и снова вернуть его значение.


private static volatile MySingleton INSTANCE;

public static MySingleton getInstance() {
    MySingleton result = INSTANCE;
    if (result == null) {
        synchronized(MySingleton.class) {
            result = INSTANCE;
            if (result == null) {
                INSTANCE = result = createInstance();
            }
        }
    }

    return result;
}

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

Я недавно написал статью об этом. Деконструкция Singleton. Вы можете найти более подробную информацию об этих примерах и пример шаблона "держатель". Существует также пример в реальном мире, демонстрирующий двойную проверку изменчивого подхода. Надеюсь, это поможет.

Ответ 9

Класс Singloton - это класс, в котором вы получаете один и тот же объект каждый раз. Если вы хотите ограничить класс от создания более чем одного объекта, нам нужен класс Singleton.

Например:

public class Booking {
    static Booking b = new Booking();
    private Booking() { }
    static Booking createObject() { return b; }
}

Чтобы создать объект этого класса, мы можем использовать:

Booking b1, b2, b3, b4;
b1 = Booking.createObject();
b2 = Booking.createObject();
Booking b1, b2, b3, b4;
b1 = Booking.createObject();
b2 = Booking.createObject();

b1 и b2 относятся к одному и тому же объекту.

Ответ 10

Вы должны думать о следующих свойствах при создании класса singleton

  1. отражение
  2. Многопоточность
  3. клон
  4. Сериализация

если в вашем классе нет интерфейсов Clone или Serialization, я думаю, что следующий класс лучше всего подходит для одного класса.

public class JavaClass1 {
private static JavaClass1 instance = null;
private JavaClass1() {
    System.out.println("Creating -------");
    if (instance != null) { // For Reflection
        throw new RuntimeException("Cannot create, please use getInstance()");
    }
}

public static JavaClass1 getInstance() {
    if (instance == null) {
        createInstance();
    }
    return instance;
}
 private static synchronized void createInstance() { // for multithreading
    if (instance == null) {
        instance = new JavaClass1();
    }
}}