Comment résoudre le “Double-Vérifier le Verrouillage est Cassé” Déclaration en Java?
Je veux mettre en œuvre l'initialisation tardive pour le multithreading en Java.
J'ai un code du genre:
class Foo {
private Helper helper = null;
public Helper getHelper() {
if (helper == null) {
Helper h;
synchronized(this) {
h = helper;
if (h == null)
synchronized (this) {
h = new Helper();
} //release inner synchronization lock
helper = h;
}
}
return helper;
}
//other functions and members...
}
Et je suis de la "Double-Vérifier le Verrouillage est Cassé" de la déclaration.
Comment puis-je résoudre ce problème?
- Double-vérifier le verrouillage a été résolu, vérifiez Efficace Java 2nd Edition pour plus de détails sur la façon de le faire.
- Vous n'avez pas besoin de l'intérieur de synchroniser déclaration, en tout cas, puisque vous êtes déjà dans une.
- C'est seulement un problème si les nouveaux Auxiliaires() ne peut jamais lever une exception. Jamais un OOME. S'il y a un nouvel Objet() dans le constructeur, alors il n'est pas un problème. Le constructeur doit également pas vide. Si le constructeur est simple, alors il est plus simple de créer l'objet n'est pas paresseux.
Vous devez vous connecter pour publier un commentaire.
Ici est l'idiome recommandé dans le Article 71: l'Utilisation d'initialisation tardive judicieusement de
Efficace Java:
De référence
result = field;
est nécessaire à l'intérieur de la SYNCHRONISATION de bloc? SYNCHRONISATION de lui-même suffisant pour mettre à jour le fil de droite? par exemple)if (field== null) // Second check (with locking)
2) de l'Affectation de la valeur de résultat est nécessaire lors de la dernière?result = computeFieldValue();
Ici est un modèle pour corriger une double vérification de verrouillage.
Pour un singleton, il est beaucoup plus lisible idiome pour l'initialisation tardive.
tmp= lazy;
est nécessaire à l'intérieur de la SYNCHRONISATION de bloc? SYNCHRONISATION de lui-même suffisant pour mettre à jour le fil de droite? afin que nous puissions vérifier que surif (lazy== null)
à l'intérieur de SYNCHRONISATION au lieuif (temp== null)
2) de l'Affectation de la valeur de résultat est nécessaire lors de la dernière?tmp = createHeavyWeightObject();
tmp
est là pour enregistrer un supplément de lire de lavolatile
variable,lazy
; l'hypothèse est que la lecture d'une variable volatile est beaucoup plus lent que le stockage d'une variable locale. Le (2) deuxième affectation prend en charge un seulreturn
, qui est une figure de style de choix pour des raisons de lisibilité.ce qui est cassé sur DCL?
DCL s'appuie sur une désynchronisation de l'utilisation de la ressource de terrain. Qui semble être inoffensif, mais il ne l'est pas. Pour comprendre pourquoi, imaginons qu'Un thread est à l'intérieur du bloc synchronisé, l'exécution de la déclaration de ressources = nouvelle Ressource(); tandis que le thread B est juste d'entrer dans getResource(). Prendre en compte l'effet sur la mémoire de cette initialisation. Mémoire pour le nouvel objet de la Ressource sera alloué; le constructeur de la Ressource sera appelé, d'initialiser les champs des membres de la nouvelle de l'objet; et le champ des ressources de SomeClass sera attribué une référence à l'objet nouvellement créé.
Cependant, depuis le thread B n'est pas de l'exécution à l'intérieur d'un bloc synchronisé, il peut voir ces opérations de mémoire dans un ordre différent de l'un thread s'exécute. Ce pourrait être le cas que B voit ces événements dans l'ordre suivant (et le compilateur est également libre de réorganiser les instructions de ce genre): allouer de la mémoire, d'attribuer la référence à la ressource, l'appel du constructeur. Supposons que le fil B vient après le mémoire a été allouée et la ressource champ est défini, mais avant que le constructeur est appelé. Il voit que la ressource n'est pas null, ignore le bloc synchronisé, et renvoie une référence à un partiellement construit de Ressources! Inutile de dire que le résultat n'est ni prévu ni souhaité.
Peut ThreadLocal de vous aider à résoudre DCL?
Nous pouvons utiliser ThreadLocal pour atteindre le DCL idiome de l'objectif explicite -- initialisation sans synchronisation sur la commune de chemin d'accès du code. Considérez ceci (thread-safe) version de DCL:
Listing 2. DCL à l'aide ThreadLocal
Je pense; ici, chaque thread une fois entre dans la SYNCHRONISATION de bloc de mise à jour de la threadLocal valeur, alors elle ne sera pas. Donc ThreadLocal DCL permettra d'assurer un thread va entrer qu'une seule fois à l'intérieur de la SYNCHRONISATION de bloc.
Qu'est synchronisé signifie vraiment?
Java traite chaque thread comme si il fonctionne sur son propre processeur avec sa propre mémoire locale de chaque parler et la synchronisation avec un partage de la mémoire principale. Même sur un système monoprocesseur, ce modèle a un sens, car les effets de mémoire cache et l'utilisation de registres du processeur pour stocker des variables. Lorsqu'un thread modifie un emplacement dans son local de la mémoire, cette modification devrait éventuellement apparaître dans la mémoire principale, et le JMM définit les règles d'lorsque la JVM pour le transfert des données entre le local et la mémoire principale. La Java des architectes réalisé qu'une conception trop restrictive de la mémoire de modèle compromettrait sérieusement les performances du programme. Ils ont tenté d'élaborer un modèle de mémoire qui permettrait à des programmes de bien performer sur le matériel informatique moderne tout en offrant des garanties qui permettrait aux fils d'interagir de manière prévisible.
Java principal outil pour le rendu des interactions entre les threads de manière prévisible est le mot-clé synchronized. De nombreux programmeurs considèrent synchronisé strictement dans les termes de l'application d'un sémaphore d'exclusion mutuelle (mutex) pour empêcher l'exécution des sections critiques en plus d'un thread à la fois. Malheureusement, cette intuition n'est pas de décrire complètement ce synchronisé moyens.
La sémantique de la synchronisation ne comprend exclusion mutuelle d'exécution en se basant sur le statut d'un sémaphore, mais ils comprennent également les règles relatives à la synchronisation des threads, l'interaction avec la mémoire principale. En particulier, l'acquisition ou la libération d'un verrou déclenche une barrière de mémoire -- une synchronisation forcée entre le fil de la mémoire locale et la mémoire principale. (Certains processeurs, comme l'Alpha -- explicite d'instructions machine pour la réalisation de barrières de mémoire.) Lorsqu'un thread s'arrête en parallèle, un bloc, il effectue une barrière d'écriture, il doit se rincer toutes les variables modifiées dans ce bloc de mémoire principale avant de relâcher le verrou. De même, lors de la saisie d'un bloc synchronisé, il effectue une lecture de la barrière -- c'est comme si la mémoire locale a été invalidé, et il doit récupérer toutes les variables qui seront référencés dans le bloc à partir de la mémoire principale.
La seule façon de faire une double vérification de verrouillage correctement en Java est d'utiliser "volatile" les déclarations sur la variable en question. Alors que la solution est correcte, note que "volatile" désigne les lignes de cache se rincé à chaque accès. Depuis "synchronisé" bouffées de chaleur à la fin du bloc, cela risque de ne pas être plus efficace (ou même moins efficace). Je le recommande tout simplement pas à l'aide d'un double contrôle de verrouillage, sauf si vous avez profilé votre code et a conclu qu'un problème de performance dans ce domaine.
Définir la variable qui doit être vérifié en double avec
volatile
midifierVous n'avez pas besoin de la
h
variable.Voici un exemple de ici
qu'entendez-vous, de qui vous êtes l'obtention de la déclaration?
Double-Vérifier le Verrouillage est fixe. consultez wikipédia:
volatile
(bien que je n'ai pas comparé ça).Comme quelques-uns l'ont noté, vous avez certainement besoin de l'
volatile
mot-clé pour le faire fonctionner correctement, à moins que tous les membres de l'objet sont déclarésfinal
, sinon il n'y a pas de passe-avant de pr coffre-publication et vous pouvez voir les valeurs par défaut.Nous est tombée malade de la constante de problèmes avec les gens obtenir cette erreur, nous avons donc codé une LazyReference utilitaire qui a le dernier sémantique et a été le sujet d'articles et d'écoute pour être aussi rapide que possible.
La copie ci-dessous à partir de quelque part d'autre ,ce qui explique pourquoi l'utilisation d'une méthode de variable locale comme une copie de la volatilité de la variable permet d'accélérer les choses.
Déclaration qui a besoin d'explication:
Explication:
Si je ne me trompe pas, il y a aussi une autre solution si nous ne voulons pas utiliser le mot clé volatile
par exemple en prenant l'exemple précédent
le test est toujours sur l'aide de la variable, mais la construction de l'objet se fait juste avant la newHelper, ça évite d'avoir un partiellement construit objet