Question

Pensez à l'idiome "double-vérification" pour l'initialisation lente des champs d'instance ":

// Item 71 in Effective Java copied from this interview with Bloch.
private volatile FieldType field;
FieldType getField() {
    FieldType result = field;
    if (result == null) { // First check (no locking)
        synchronized(this) {
            result = field;
            if (result == null) // Second check (with locking)
                field = result = computeFieldValue();
        }
    }
     return result;
}

Je souhaite pouvoir réinitialiser le champ de manière sécurisée (le forcer à se recharger à partir de la base de données, dans mon cas). Je suppose que nous pourrions le faire en utilisant une méthode de réinitialisation:

void reset() {
   field = null;
}

Est-ce la méthode standard pour réinitialiser le champ? Est-ce sûr? Des pièges? Je pose la question parce que Bloch a émis l’avertissement suivant concernant le chargement paresseux à double vérification: "L’idiome est très rapide mais il est aussi compliqué et délicat, ne soyez donc pas tenté de le modifier de quelque manière que ce soit. Il suffit de copier et coller - normalement pas une bonne idée, mais approprié ici. "

Merci d'avance, Playa de l'Himalaya.

Était-ce utile?

La solution

Oui, c'est un thread sûr.

Le bloc synchronisé doit empêcher plusieurs threads d'appeler inutilement computeFieldValue () . Puisque le champ est volatile, les accès dans reset et getField sont tous bien ordonnés.

Si la première vérification est non nulle, getField est terminé; résultat est renvoyé.

Sinon, un verrou est acquis, excluant tout autre thread pouvant définir le champ comme non nul, mais autorisant tout thread à définir le champ sur null. Si un thread ne définit pas field sur null, rien ne devrait avoir changé. c'est la condition qui a introduit le fil dans le bloc synchronisé. Si un autre thread a déjà acquis le verrou après la vérification du thread actuel et que le champ est défini sur une valeur non nulle, la deuxième vérification le détectera.

Autres conseils

Je pense que cela devrait être sûr, mais uniquement parce que vous stockez le champ dans une variable locale. Une fois cette opération effectuée, il est impossible pour la référence de variable locale de passer magiquement à null, même si un autre thread réinitialise la valeur du champ à mi-parcours.

Il semble que cela fonctionnera tant que la méthode de réinitialisation est la méthode reset () indiquée ci-dessus. Cependant, si la méthode reset () instancie un nouvel objet (comme ci-dessous), ne pourriez-vous pas renvoyer potentiellement quelque chose de différent de ce que vous aviez prévu?

void reset() {
    field = new FieldType();
}

Je suppose que cela dépend de ce que vous entendez par thread-safe.

Vous pourriez vous retrouver dans une situation où une première instance est utilisée après une seconde. Cela peut être correct ou non.

Je pense que la méthode reset () n'est pas correcte. Si vous lisez le point 71, vous trouverez:

Ce code peut paraître un peu compliqué. En particulier, la nécessité pour le local résultat variable peut ne pas être clair. Ce que cette variable fait est de s'assurer que ce champ est lecture seule une fois dans le cas habituel où il est déjà initialisé.

L’initialisation paresseuse ne suppose pas que le champ puisse être modifié. Si le champ sera défini sur null entre ces opérateurs:

FieldType result = field;
if (result == null) {// Première vérification (pas de verrouillage)

getField () fournit un résultat incorrect.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top