pourquoi utiliser volatile avec un bloc synchronisé ?
-
12-12-2019 - |
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.
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
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)