2011-02-07 1 views
14

Il est indiqué que l'affectation des champs est toujours atomique, sauf pour les champs de long ou double.Pourquoi le verrouillage à double vérification est-il cassé en Java?

Mais, quand je lis une explication des raisons pour lesquelles le verrouillage à double contrôle est cassé, il est dit que le problème est en opération d'affectation:

// Broken multithreaded version 
// "Double-Checked Locking" idiom 
class Foo { 
    private Helper helper = null; 
    public Helper getHelper() { 
     if (helper == null) { 
      synchronized(this) { 
       if (helper == null) { 
        helper = new Helper(); 
       } 
      } 
     } 
     return helper; 
    } 

    // other functions and members... 
} 
  1. Discussion A avis que la valeur n'est pas initialisé , donc il obtient le verrou et commence à initialiser la valeur .
  2. En raison de la sémantique de certains langages de programmation, le code généré par le compilateur est autorisé pour mettre à jour la variable partagée pour pointer vers un objet partiellement construit avant A a fini d'effectuer l'initialisation .
  3. Le thread B remarque que la variable partagée a été initialisée (ou alors apparaît) et renvoie sa valeur. Étant donné que thread B croit que la valeur est déjà initialisé, il pas acquérir le verrou. Si B utilise l'objet avant tout l'initialisation fait par A est vu par B (soit parce qu'un n'a pas terminé l'initialisation ou parce que certaines des valeurs initialisées dans l'objet ont pas encore percolé à la mémoire B utilise (cache cohérence)), le programme va probablement crash.
    (à partir de http://en.wikipedia.org/wiki/Double-checked_locking).

Quand est-ce possible? Est-il possible que l'opération d'assignation JVM 64 bits ne soit pas atomique? Si non alors si le "double-checked locking" est vraiment cassé?

+1

Aucune différence sur les JVM 64 bits. – rapadura

+0

duplicata possible de http://stackoverflow.com/questions/12448864/java-double-locking-can-someone-explain-more-simply-why-intuition-wouldnt-wor/12449110?noredirect=1#comment26446551_12449110 –

Répondre

15

Le problème n'est pas l'atomicité, c'est la commande. La JVM est autorisée à réorganiser les instructions afin d'améliorer les performances, tant que happens-before n'est pas violé. Par conséquent, le moteur d'exécution peut théoriquement planifier l'instruction qui met à jour helper avant que toutes les instructions du constructeur de la classe Helper aient été exécutées.

+0

il commande toujours, n'est-ce pas :). et c'est la bonne réponse – bestsss

2

Plusieurs affectations peuvent être nécessaires pour construire l'instance de Helper à l'intérieur du constructeur, et la sémantique permet de les réorganiser par rapport à l'affectation helper = new Helper(). Par conséquent, le champ helper peut recevoir une référence à un objet lorsque toutes les affectations n'ont pas été effectuées, de sorte qu'il est incomplètement initialisé.

+0

quoi faire vous voulez dire "plusieurs missions"? Je vois seulement 1: 'helper = new Helper()'. Si elle peut être construite "en place", pourriez-vous donner un lien qui prouve que java fait vraiment des choses si étranges? THX. – Roman

+1

@Roman: Comme mentionné dans les guillemets dans votre question, le code généré par le compilateur est autorisé à assigner 'Helper' à une instance de' Helper' qui n'a pas terminé la construction_. – ColinD

7

L'affectation de la référence est atomique, mais la construction n'est pas! Comme indiqué dans l'explication, supposons que le thread B veut utiliser le singleton avant que le thread A ne l'ait entièrement construit, il ne peut pas créer une nouvelle instance car la référence n'est pas nulle, donc il renvoie juste l'objet partiellement construit.

Si vous ne vous assurer que la publication la référence partagée se produit avant une autre charge de fil qui ont partagé de référence, la écriture de la référence au nouvel objet peut être réorganisés avec les écritures à ses champs .Dans ce cas, un autre thread pouvait voir une valeur à jour pour la référence d'objet mais des valeurs obsolètes pour tout ou partie de l'objet de l'objet - un objet partiellement construit. - Brian Goetz: Java Concurrency en pratique

Puisque la vérification initiale de null n'est pas synchronisée, il n'y a pas de publication et cette réorganisation est possible.

+0

est-il indiqué n'importe où que java fait l'affectation d'abord et ensuite seulement calcule la bonne partie? (C'est un peu le modèle 'Future', mais je n'ai jamais vu de déclarations officielles sur son utilisation par le compilateur java). – Roman

+0

@Roman: Ce n'est pas une simple question de "Java affecte d'abord et ensuite seulement calcule la bonne partie". Ce serait une déclaration incorrecte, je pense. Java permet au code de bas niveau de réorganiser les instructions d'une manière qui peut provoquer des résultats inattendus lorsqu'un champ partagé qui n'est pas «volatile» ou «final» est accédé par un autre thread. – ColinD

+0

@Roman voir la mise à jour ... – Robert

0

Je suis désolé que ce soit un peu hors de propos pour la question, je suis juste curieux. Dans ce cas, ne vaut-il pas mieux acquérir le verrou avant l'affectation et/ou renvoyer la valeur? Comme:

private Lock mLock = new ReentrantLock(); 
private Helper mHelper = null; 

private Helper getHelper() { 
    mLock.lock(); 
    try { 
     if (mHelper == null) { 
      mHelper = new Helper(); 
     } 
     return mHelper; 
    } 
    finally { 
     mLock.unlock(); 
    } 
} 

Ou y a-t-il un avantage quelconque à utiliser le verrouillage à double contrôle?

-1
/*Then the following should work. 
    Remember: getHelper() is usually called many times, it is BAD 
    to call synchronized() every time for such a trivial thing! 
*/ 
class Foo { 

private Helper helper = null; 
private Boolean isHelperInstantiated; 
public Helper getHelper() { 
    if (!isHelperInstantiated) { 
     synchronized(this) { 
      if (helper == null) { 
       helper = new Helper(); 
       isHelperInstantiated = true; 
      } 
     } 
    } 
    return helper; 
} 

// other functions and members... 
}  
Questions connexes