web-dev-qa-db-fra.com

Initialisation paresseuse incorrecte

Findbug m'a dit que j'utilisais une initialisation paresseuse incorrecte.

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

    instance = new Object();
    return instance;
}

Je ne vois rien de mal ici. Est-ce un mauvais comportement de findbug ou j'ai raté quelque chose?

53
michael nesterenko

Findbug fait référence à un problème de thread potentiel. Dans un environnement multi-thread, il serait possible que votre singleton soit créé plus d'une fois avec votre code actuel.

Il y a beaucoup de lecture ici , mais cela aidera à expliquer.

La condition de course est ici sur le if check. Au premier appel, un thread entrera dans le if check, et créera l'instance et l'affectera à 'instance'. Mais il est possible qu'un autre thread devienne actif entre le if check et la création/affectation de l'instance. Ce fil pourrait également passer le if check car l'affectation n'a pas encore eu lieu. Par conséquent, deux instances (ou plus, si davantage de threads entraient) seraient créées et vos threads auraient des références à différents objets.

60
nicholas.hauschild

Votre code est légèrement plus complexe que nécessaire, ce qui pourrait expliquer pourquoi il est confus.

Edit: C'est certainement le problème de filetage comme les autres l'ont signalé, mais j'ai pensé publier la mise en œuvre de la vérification du double verrouillage ici pour référence ci-dessous:

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;
}
18
JohnKlehm

NOTE : La solution de vérification de double verrouillage de JohnKlehm est meilleure. Laissant cette réponse ici pour des raisons historiques.

Cela devrait être

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

    return instance;
}
11
Varun Achar

Vous devez verrouiller l'instanciation pour que cela soit correct

LI: initialisation paresseuse incorrecte du champ statique (LI_LAZY_INIT_STATIC)

Cette méthode contient une initialisation paresseuse non synchronisée d'un champ statique non volatile. Étant donné que le compilateur ou le processeur peut réorganiser les instructions, les threads ne sont pas garantis de voir un objet complètement initialisé, si la méthode peut être appelée par plusieurs threads. Vous pouvez rendre le champ volatile pour corriger le problème. Pour plus d'informations, consultez le site Web Java Memory Model).

4
antlersoft

Vous avez manqué un problème de multi-threading,

private static Object instance;

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

Merci à John Klehm pour l'échantillon publié

peut également essayer d'affecter directement une instance d'objet dans un bloc synchronisé

synchronized (MyCurrentClass.myLock=new Object())

c'est à dire.

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;

}
2
jdeveloper

votre objet statique n'est pas synchronisé. De plus, votre méthode n'est pas une initialisation paresseuse. Normalement, vous conservez une carte d'objet et vous initialisez celle souhaitée à la demande. Donc, vous ne les initialisez pas tous au début plutôt que de les appeler quand cela est nécessaire (appelé).

0
Shahriar

Depuis 1.5: l'instance doit être volatile et vous devez intégrer une variable tmp pour éviter d'utiliser une instance créée mais son initialisation n'est pas encore terminée.

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;

}
0
user8517281