Question

J'ai vu quelques exemples en Java où ils effectuent une synchronisation sur un bloc de code pour modifier une variable alors que cette variable était déclarée volatile à l'origine ..J'ai vu cela dans un exemple de classe singleton où ils ont déclaré l'instance unique comme volatile et ils ont synchronisé le bloc qui initialise cette instance...Ma question est la suivante : pourquoi nous le déclarons volatile pendant que nous nous synchronisons dessus, pourquoi devons-nous faire les deux ?l'un d'eux n'est-il pas suffisant pour l'autre ??

public class someClass {
volatile static uniqueInstance = null;

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

Merci d'avance.

Était-ce utile?

La solution

La synchronisation elle-même serait suffisante dans ce cas si le premier chèque était dans un bloc synchronisé (mais ce n'est pas et un thread peut ne pas voir les modifications effectuées par un autre si la variable n'était pas volatile).Volatile seul ne suffirait pas parce que vous devez effectuer plus d'une opération atomique.Mais méfiez-vous!Ce que vous avez ici est dit de verrouillage dits à double vérification - un idiome commun, qui malheureusement ne fonctionne pas de manière fiable .Je pense que cela a changé depuis Java 1.6, mais ce type de code peut être risqué.

edit : Lorsque la variable est volatile, ce code fonctionne correctement depuis JDK 5 (pas 6 comme je l'ai écrit plus tôt), mais cela ne fonctionnera pas comme prévu sous JDK 1.4 ou plus tôt.

Autres conseils

Cela utilise le verrouillage à double vérification, notez que le if(uniqueInstance == null) n'est pas dans la partie synchronisée.

Si uniqueInstance n'est pas volatile, il peut être "initialisé" avec un objet partiellement construit dont certaines parties ne sont visibles que par le thread s'exécutant dans le synchronized bloc.volatile en fait une opération tout ou rien dans ce cas.

Si vous n'aviez pas le bloc synchronisé, vous pourriez vous retrouver avec 2 threads arrivant à ce point en même temps.

if(uniqueInstance == null) {
      uniqueInstance = new someClass(); <---- here

Et vous construisez 2 objets SomeClass, ce qui va à l’encontre de l’objectif.

À proprement parler, vous n'avez pas besoin de volatile , la méthode aurait pu être

public static someClass getInstance() {
    synchronized(FullDictionary.class) {
         if(uniqueInstance == null) {
             uniqueInstance = new someClass();
          }
         return uniqueInstance;
    }
}

Mais cela implique la synchronisation et la sérialisation de chaque thread qui exécute getInstance().

Cet article explique l'idéederrière volatile.

Il est également adressé dans le travail séminal, Java Concurrence dans la pratique .

L'idée principale est que la concurrence implique non seulement de la protection de l'état partagé, mais également de la visibilité de cet état entre les threads: c'est là que la volatilité est disponible. (Ce contrat plus important est défini par le Modèle de mémoire Java .)

Vous pouvez faire la synchronisation sans utiliser de bloc synchronisé. Ce n'est pas nécessaire d'utiliser une variable volatilité ... Volatile met à jour la variable de la mémoire principale..et Mise à jour synchronisée Toutes les variables partagées accessibles à partir de la mémoire principale. Donc, vous pouvez l'utiliser selon vos besoins.

Mes deux cents ici

fristère une explication rapide de l'intuition de ce code

if(uniqueInstance == null) {
        synchronized(someClass.class) {
            if(uniqueInstance == null) {
                uniqueInstance = new someClass();
            }
        }
    }

La raison pour laquelle il vérifie un treeCielStance== NULL deux fois consiste à réduire les frais généraux d'appeler le bloc synchronisé qui est relativement plus lent.Tellement appelé verrouillage à double vérification.

Deuxièmement, la raison pour laquelle il utilise synchronisé est facile à comprendre, il rend les deux opérations à l'intérieur de l'atomique synchronisé.

Enfin, le dernier modificateur volatil garantissait que toutes les threads voient la même copie de sorte que la première vérification à l'extérieur du bloc synchronisé verra la valeur de OneSeCeinstance d'une manière «synchronisée». avec le bloc synchronisé.Sans le filificateur volatil, un thread peut affecter une valeur à un point d'accès mais l'autre thread peut ne pas le voir par le premier chèque.(Bien que le deuxième chèque le verra)

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