Синглтон на основе DCL

323
14 февраля 2017, 18:20

Существует такая реализация синглтона на основе double checked locking (пункт 2):

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

Вопрос 1: для чего здесь используется localInstance? Почему бы не сделать вот так:

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

?

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

Вопрос 2: Почему в данном классе не объявлен приватный конструктор? Ведь в случае его отсутствия где-то в коде можно явно создать экземпляр данного класса.

Answer 1

Это сделано, отчасти из за производительности, отчасти из за того, что если не использовать дополнительной локальной переменной, то код может работать не корректно.

А именно, если мы напишем в synchronized :

instance = new Singleton();

А Singleton будет иметь, например, такую структуру:

class Singleton {
    private Object obj;
    Singleton(){
       obj = new Object();
    }
}

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

  1. Создается объект Singleton
  2. Присваивается значению instance
  3. Инициализируются поля у созданного объекта

Такая ситуация называется небезопасной публикацией. Чтобы ее исправить достаточно, определить поля либо final, либо volatile (есть еще варианты).

Хорошая статья с примерами на данную тему

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

Answer 2

localInstance нужен для повышения производительности этого кода.

В случаях, когда instance уже инициализирован(т.е. в большинстве случаев)
это позволяет не обращаться к volatile полю второй раз,
при return instance вместо return localInstance

Источник: Joshua Bloch "Effective Java, Second Edition", p. 283-284
Там он утверждает, что это позволяет добиться прироста производительности в 25%

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

READ ALSO
Реализовать getMapAsync() в классе

Реализовать getMapAsync() в классе

Есть активити в которой реализую метод getMap(), но так как он depricated я его не могу реализовать в полной мереНашёл несколько вариантов решения...

393
Как отслеживать перемотку ListView

Как отслеживать перемотку ListView

Это мой класс наследованный от SimpleCursorAdapterЗаливаю базу данных в listview

491
Как не терять фокус?

Как не терять фокус?

Дело в том что у меня есть Видео Плеер использую библиотеку и перед ним ImageView, то есть типа баннера, которая закрывает 30% видеоплеераКаждые...

363
Чтение данных из Socket без задержки

Чтение данных из Socket без задержки

Имеется приложение, логика которого следующая: 1) Приложение подключается к серверу2) При нажатии на кнопку приложение читает данные от сервера

365