web-dev-qa-db-fra.com

pourquoi utiliser volatile avec bloc synchronisé?

J'ai vu quelques exemples en Java où ils synchronisent un bloc de code pour changer une variable alors que cette variable était déclarée volatile à l'origine. cela initialise cet exemple ... Ma question est de savoir pourquoi nous le déclarons volatil pendant que nous synchronisons dessus, pourquoi devons-nous faire les deux ?? l'un d'eux ne suffit-il pas à 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.

39
Dorgham

La synchronisation par elle-même suffirait dans ce cas si la première vérification était dans le bloc synchronisé (mais ce n'est pas le cas et un thread peut ne pas voir les modifications effectuées par un autre si la variable n'est pas volatile). Volatile seul ne serait pas suffisant car vous devez effectuer plusieurs opérations de manière atomique. Mais méfiez-vous! Ce que vous avez ici est ce qu'on appelle un verrouillage à 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 toujours être risqué.

EDIT: lorsque la variable est volatile, ce code fonctionne correctement depuis le JDK 5 (et non le 6 comme je l'ai écrit précédemment), mais il ne fonctionnera pas comme prévu dans le JDK 1.4 ou antérieur.

17
Michał Kosmulski

Ceci utilise le double verrouillage vérifié, notez que la if(uniqueInstance == null) ne se trouve 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 fil d'exécution du bloc synchronized. volatile rend cette 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 annule le but.

Strictement parlant, 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 exécutant getInstance ().

6
nos

Ce post explique l'idée derrière volatile.

Il est également traité dans le travail fondateur, Java Concurrency in Practice .

L'idée principale est que la simultanéité implique non seulement la protection de l'état partagé, mais également la visibilité de cet état entre les threads: c'est là que réside la volatilité. (Ce contrat plus important est défini par Java Memory Model .)

4
Michael Easter

Vous pouvez effectuer la synchronisation sans utiliser de bloc synchronisé . Il n'est pas nécessaire d'utiliser une variable volatile dans celui-ci .... accessible depuis la mémoire principale ... vous pouvez donc l'utiliser en fonction de vos besoins.

0
user1237385