Pourquoi ne peut-Java constructeurs être synchronisées?
Selon la Spécification du Langage Java, les constructeurs ne peuvent pas être marquées synchronisé parce que les autres threads ne peuvent pas voir l'objet en cours de création jusqu'à ce que le fil de la création, il l'a terminé. Cela semble un peu bizarre, parce que je ne peux en effet avoir un autre thread voir l'objet, alors qu'il est en cours de construction:
public class Test {
public Test() {
final Test me = this;
new Thread() {
@Override
public void run() {
//... Reference 'me,' the object being constructed
}
}.start();
}
}
Je sais que c'est un joli exemple artificiel, mais il semble que dans la théorie que quelqu'un pourrait trouver une façon plus réaliste cas où le marquage le constructeur synchronisé serait légitime afin de prévenir les courses avec son fils comme celui-ci.
Ma question est la suivante: est-il une raison que Java serait d'interdire le modificateur synchronized sur un constructeur? Peut-être que mon exemple ci-dessus est erronée, ou peut-être il n'y a vraiment aucune raison et c'est une conception arbitraire de la décision. Dans les deux cas, je suis très curieux et j'aimerais vraiment connaître la réponse.
- En aparté, il est fortement recommandé de ne pas autoriser le "ce" de référence pour s'échapper avant que le constructeur a terminé.
- Q - j'ai entendu cela avant, mais ne comprennent pas bien pourquoi. Est-il une raison particulière? J'ai pu voir de Mauvaises Choses qui se passe si vous avez donné une référence à cette avant que vous avez terminé l'initialisation de l'objet, mais que si c'est la dernière chose que vous faites dans le constructeur?
- c'est vraiment l'objet d'une autre question, mais même si c'est la dernière chose que vous faites dans le constructeur, si l'objet est sous-classé, puis la sous-classe n'a pas fini de construire. Si cette classe est définitive, et vous n'avez pas la chaîne de constructeurs (à l'appel de cette(...)) et de faire autre chose après la chaîne appel et c'est la dernière chose que vous faites, de son amende. Sauf, bien sûr, que l'une de ces décisions sont susceptibles de changer (vous pouvez ajouter un deuxième constructeur plus tard).
- C'est beaucoup plus clair si vous citer ce que le JLS dit en réalité: "Il n'y a pas de pratique besoin d'un constructeur de la synchronisation, car elle permettrait de verrouiller l'objet en cours de construction, qui est normalement ne sont pas disponibles pour les autres threads jusqu'à ce que tous les constructeurs de l'objet, ont terminé leur travail."
- voir ibm.com/developerworks/java/library/j-jtp0618.html pour plus de détails
- De la Java Tutoriel (lien) "Attention: Lors de la construction d'un objet qui sera partagé entre les threads, faire très attention à ce qu'une référence à l'objet n'a pas de "fuite" prématurément. Par exemple, supposons que vous voulez maintenir une Liste appelée instances contenant toutes les instances d'une classe. Vous pourriez être tenté d'ajouter la ligne suivante à votre constructeur:
instances.add(this);
Mais alors les autres threads peuvent utiliser des instances d'accès à l'objet avant la construction de l'objet est terminée." - En plus de la remarque précédente, la Java Tutoriel dit, dans la même leçon, que vous ne pouvez pas utiliser le " synchronisé mot-clé dans un constructeur, puis il indique le message d'avertissement dans le commentaire précédent. Cela semble contradictoire pour moi, et j'espère que, dans l'avenir, les constructeurs seront synchronisés par défaut sur l'objet créé. C'est ce qui semble le plus raisonnable à mon humble avis, car, autrement, vous accédez à un objet dans un état incohérent.
Vous devez vous connecter pour publier un commentaire.
Si vous avez vraiment besoin de synchronisation du reste du constructeur contre tout les threads qui, de toute façon, obtient une référence à votre n'est pas encore totalement construit un objet, vous pouvez utiliser une synchronisation de bloc:
Il est généralement considéré mauvais style de "donner" votre pas-encore-objet construit, donc, en parallèle, un constructeur n'est pas nécessaire.
Dans certains cas de coin en parallèle, un constructeur serait utile. Voici un exemple plus réaliste exemple, à partir de la discussion de Bozho réponse:
Nous voulons que le
doSomethingDangerous()
méthode n'est appelée qu'après la construction de notre objet sous-classe est complète, par exemple, nous voulons seulement le "tout est OK" de sortie. Mais dans ce cas, lorsque vous ne pouvez modifier votre sous-classe, vous n'avez aucune chance d'y parvenir. Si le constructeur pourrait être synchronisée, elle permettrait de résoudre le problème.Donc, ce que nous apprenons à ce sujet: ne jamais faire quelque chose comme je l'ai fait ici dans le constructeur de la superclasse, si votre classe n'est pas définitive et ne pas appeler n'importe quel non-finale méthodes de votre classe à partir de votre constructeur.
abstract
. Le second, en cas de besoin, bien que – je l'ai ajouté ici.La question a été soulevée sur une liste de discussion utilisé par les rédacteurs de la Java simultanées de l'API et la Java du Modèle de Mémoire. Plusieurs réponses ont été données, en particulier Hans Boehm a répondu:
static MyObject o;
- 1 thread exécuteo = new MyObject();
tandis que le thread 2 appelso.getSomething();
. Il y a une condition de course et le thread 2 pu observer une partie objet construit. Si le constructeur etgetSomething
ont été synchronisés, vous avez la garantie de voir l'objet dans un état cohérent. D'où le commentaire dans la citation ci-dessus "le code Client ne doit pas utiliser des courses pour communiquer la référence, donc il ne devrait pas.".Parce que
synchronized
garantit que les actions sur les mêmes objets ne doivent pas être exécutées par plusieurs threads. Et lorsque le constructeur est appelé vous ne l'avez pas encore l'objet. Il est logiquement impossible que deux threads d'accéder au constructeur de l'objet même.Dans votre exemple, même si une méthode est appelée par le nouveau thread, il n'est plus sur le constructeur - c'est à propos de l'objectif méthode en cours de synchronisation ou pas.
synchronized
Dans votre exemple, le constructeur n'est appelé qu'une seule fois à partir d'un thread.
Oui, il est possible d'obtenir une référence à une incomplètement Objet construit (certaines discussions autour d'un double contrôle de verrouillage et pourquoi il est cassé révéler ce problème), cependant, pas en appelant le constructeur une deuxième fois.
Synchronisées sur le constructeur de prévenir les deux fils de l'appel du constructeur, sur le même Objet en même temps, et ce n'est pas possible, comme il n'est jamais possible d'appeler le constructeur sur une instance de l'objet à deux reprises, période.
Constructeur de la section de Modificateurs dans JLS dit clairement
Donc il n'est pas nécessaire pour le constructeur à être synchronisés.
Aussi il n'est pas recommandé de donner les objets de référence(ce) avant la création de l'objet. Une des situations ambiguës serait de donner les objets de référence est le constructeur de la superclasse quand subsclass objet est créé.
Je vois peu de raisons d'interdire les constructeurs à être synchronisés. Il serait utile dans de nombreux scénarios dans les applications multi-thread. Si je comprends la Java de la Mémoire de Modèle correcte (j'ai lu http://jeremymanson.blogspot.se/2008/11/what-volatile-means-in-java.html) le simple classe pourrait avoir bénéficié d'un synchronisé constructeur.
En théorie, je crois qu'un appel à
print()
pourrait impression de "0". Cela peut se produire si une instance de A est créé par le Thread 1, la référence de l'instance est partagée avec le Thread 2, et le Thread 2 appelsprint()
. Si il n'y a pas de synchronisation entre l'écrituremyInt = 3
de Fil 1 et le lire de la même champ par le Thread 2 Thread 2 n'est pas assuré de voir l'écriture.En parallèle, un constructeur de corriger ce problème. Suis-je le droit à ce sujet?
synchronized (this) { myInt = 3; }
dans votre constructeur pour résoudre le problème, ou la marquemyInt
comme définitive.myInt
variable avecvolatile
mot-clé.synchronized
bloc et obtenir le même effet.myInt
champ vit toujours dans un registre, pas dans la mémoire. Donc, dans l'autre thread prêt à 0.Le code suivant peut atteindre le résultat attendu pour la synchronisation de constructeur.
Une telle synchronisation peut avoir du sens dans certains cas très rares, mais je pense, c'est juste pas la peine:
En cas de doute, laisser.
Remarque que les constructeurs ne peuvent pas être synchronisés, en utilisant le synchronizedkeyword avec un constructeur est une erreur de syntaxe. La synchronisation des constructeurs n'a pas de sens, parce que seul le thread qui crée un objet devrait y avoir accès alors qu'il est en cours de construction.