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

Неправильная ленивая инициализация

Findbug сказал мне, что я использую неправильную ленивую инициализацию.

public static Object getInstance() {
    if (instance != null) {
        return instance;
    }

    instance = new Object();
    return instance;
}

Я не вижу здесь ничего плохого. Это неправильное поведение findbug, или я что-то пропустил?

4b9b3361

Ответ 1

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

Здесь много читается , но это поможет объяснить.

Состояние гонки здесь находится на if check. При первом вызове поток будет попадать в if check и создаст экземпляр и назначит его "экземпляру". Но есть потенциал для того, чтобы другой поток стал активным между if check и созданием/присваиванием экземпляра. Этот поток также может передать if check, потому что назначение еще не выполнено. Таким образом, будут созданы два (или более, если больше потоков), и ваши потоки будут ссылаться на разные объекты.

Ответ 2

Ваш код немного сложнее, чем нужно, и поэтому может быть смущен.

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

private static final Object lock = new Object();
private static volatile Object instance; // must be declared volatile

public static Object getInstance() {
    if (instance == null) { // avoid sync penalty if we can
        synchronized (lock) { // declare a private static Object to use for mutex
            if (instance == null) {  // have to do this inside the sync
                instance = new Object();
            }
        }
    }

    return instance;
}

Ответ 3

ПРИМЕЧАНИЕ: лучшее решение проверки двойного замка JohnKlehm. Оставляя этот ответ здесь по историческим причинам.

На самом деле это должно быть

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

    return instance;
}

Ответ 4

Вам нужно поместить блокировку для создания экземпляра, чтобы сделать это правильно

LI: некорректная ленивая инициализация статического поля (LI_LAZY_INIT_STATIC)

Этот метод содержит несинхронизированную ленивую инициализацию нестабильное статическое поле. Поскольку компилятор или процессор могут переупорядочить инструкции, потоки не гарантируют, что вы увидите полностью инициализированный объект, если метод может быть вызван несколькими потоками. Вы можете сделать поле volatile, чтобы исправить проблему. Для большего информацию см. на веб-сайте модели памяти Java.

Ответ 5

Благодаря John Klehm за опубликованный образец

также может попытаться напрямую назначить экземпляр объекта в синхронизированном блоке

synchronized (MyCurrentClass.myLock=new Object())

то есть.

private static volatile Object myLock = new Object();

public static Object getInstance() {
    if (instance == null) { // avoid sync penalty if we can
        synchronized (MyCurrentClass.myLock**=new Object()**) { // declare a private static Object to use for mutex
            if (instance == null) {  // have to do this inside the sync
                instance = new Object();
            }
        }
    }

    return instance;

}

Ответ 6

Вы пропустили проблему с несколькими потоками,

private static Object instance;

public static synchronized Object getInstance() {
    return (instance != null ? instance : (instance = new Object()));
}

Ответ 7

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

Ответ 8

Начиная с версии 1.5: экземпляр должен быть volatile и yould интегрировать переменную tmp, чтобы избежать использования созданного экземпляра, но его инициализация еще не завершена.

private static volatile Object myLock = new Object();
private static volatile Object instance;

public static Object getInstance() {
    if (instance == null) {
        Object tmpObj;
        synchronized (myLock) {
            tmpObj = instance;
            if (tmpObj == null) {
                tmpObj = new Object();
            }
        }
        instance = tmpObj;
    }

    return instance;

}