Есть ли функциональная разница между инициализацией синглтона в методе getInstance () или в определении переменной экземпляра - PullRequest
0 голосов
/ 10 ноября 2018

Есть ли функциональная разница между этими двумя способами реализации Singleton?

public class MySingleton {
    private static MySingleton instance;

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


public class MySingleton {
    private static final MySingleton instance = new MySingleton();

    public static MySingleton getInstance() {
        return instance;
    }
}

Кроме того, что первый способ позволил бы использовать метод clearInstance (). Хотя вы можете просто сделать экземпляр не финальным во втором методе.

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

Ответы [ 5 ]

0 голосов
/ 10 ноября 2018

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

Накладные расходы на потокобезопасное создание могут быть минимизированы с помощью шаблона двойной проверки:

private static volatile MySingleton instance;

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

Таким образом, только редкая ситуация, когда два (или более) потока обращаются к синглтону впервые (и одновременно), может войти в состояние блокировки. Впоследствии первое «if null» вернет false, и вы никогда больше не войдете в состояние блокировки.

Важно: член должен быть объявлен как volatile, чтобы этот шаблон работал надежно.

Примечание: Было доказано, что приведенная выше схема "двойной проверки блокировки" не является надежной на 100 процентов. См. Обсуждение ниже в комментариях и особенно Арктика Брайана Гетца

0 голосов
/ 10 ноября 2018

Первый метод, который вы используете, не является потокобезопасным. Я бы посчитал это ошибкой.

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

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

public class MySingleton {
    private static final Object mylock = new Object();
    private static MySingleton instance;

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

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

Оба метода будут инициализировать Singleton лениво. В Java все инициализаторы переменных и статические конструкторы задействуются загрузчиком классов при использовании класса, а не в начале кода. Если ваш путь к коду никогда не вызывает getInstance, Singleton никогда не будет инициализирован.

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

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

0 голосов
/ 10 ноября 2018

Это о Ленивой Инициализации против Стремительной инициализации. Разница в том, что в первом случае экземпляр не будет создан до тех пор, пока вы не вызовете метод getInstance(), но во втором он уже был создан еще до того, как вы вызвали метод getInstance().

Пожалуйста, перейдите по этой ссылке, если вы хотите больше информации

0 голосов
/ 10 ноября 2018

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

0 голосов
/ 10 ноября 2018

Есть ли функциональная разница между этими двумя способами реализации Singleton?

Да. Если вы используете инициализатор в объявлении переменной, то экземпляр создается при инициализации класса, даже если к экземпляру никогда не осуществляется доступ. Если вы инициализируете его в методе getInstance(), то экземпляр создается только при обращении к нему. Это имеет последствия для безопасности потоков. В противном случае это не имеет большого значения, если инициализация экземпляра обходится дешево и не имеет длительных внешних побочных эффектов, но это не всегда так.

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

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

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