Двойная проверка блокировки без использования volatile-ключевого слова и без синхронизации всего метода getInstance () - PullRequest
0 голосов
/ 24 мая 2018

Ниже приведен мой одноэлементный класс, в котором я использую двойную проверку-блокировку без использования ключевого слова volatile и без синхронизации всего метода getInstance ():

public class MySingleton {

    private static MySingleton mySingleton;

    public static MySingleton getInstance() {
        if(mySingleton == null) {
            synchronized(MySingleton.class) {
                if(mySingleton == null) {
                    MySingleton temp = new MySingleton();
                    mySingleton = temp;
                }
            }
        }

        return mySingleton;
    }
}

По моему мнению, это потокобезопасно.Если кто-то думает, что это не потокобезопасно, может кто-нибудь объяснить, почему он / она думает, что это не потокобезопасно?Спасибо.

Ответы [ 3 ]

0 голосов
/ 24 мая 2018

Я не знал об этой проблеме, пока не прочитал все комментарии.Проблема в том, что различные процессы оптимизации (компилятор, Hot Spot и т. Д.) Переписывают код.Ваше временное решение может быть легко удалено.Мне трудно поверить, что конструктор может вернуть частичный объект, но если знающие авторы так говорят, я бы доверял их мнению.

0 голосов
/ 24 мая 2018

Да, но я использую переменную "temp".Разве это не решает проблему "частично созданного объекта"?

Нет.Это не так.

Предположим, что какой-то поток A вызывает getInstance() и в итоге создает новый экземпляр и присваивает переменную mySingleton.Затем приходит поток T, вызывает getInstance() и видит, что mySingleton не null.

На данный момент поток T не использовал никакой синхронизации.Без синхронизации спецификация языка Java (JLS) не требует, чтобы поток T видел назначения, сделанные потоком A, в том же порядке, в котором их сделал поток A.

Предположим, что у объекта-одиночки есть некоторые переменные-члены.Поток A, очевидно, должен инициализировать эти переменные, прежде чем сохранить ссылку в mySingleton.Но JLS позволяет потоку T видеть mySingleton != null и все же видеть переменные-члены в их неинициализированном состоянии.На некоторых многоядерных платформах это действительно может произойти таким образом.

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

0 голосов
/ 24 мая 2018

Здесь у нас есть synchronized запись и non-synchronized чтение.У нас нет никакой гарантии, что чтение получит значение, установленное записью.Так что этот код глючит.Но это очень тонкая ошибка, особенно в многоядерных процессорах.

Если вам по какой-то причине нужно продолжить работу с приведенным выше кодом, одно из решений - объявить переменную как volatile следующим образом:

private static volatile MySingleton mySingleton;

Или сделать чтениепеременная synchronized.Любой из них решит вашу проблему за счет производительности.

Еще одним важным моментом является то, что ваша переменная temp здесь не используется.Это просто избыточный код и ничего не делает.Вместо этого непосредственно присвойте ее переменной mySingleton следующим образом:

mySingleton = new MySingleton();

Все вышеперечисленные проблемы можно легко решить, используя тип enum для создания синглетонов.Поскольку перечисления изначально Serializable, нам не нужно реализовывать их с помощью интерфейса Serializable.Проблема отражения также не существует.Поэтому на 100% гарантируется, что в JVM присутствует только один экземпляр Singleton.Таким образом, этот метод рекомендуется как лучший метод создания синглетонов в Java.

...