Pourquoi faut-il attendre() toujours être synchronisé bloc
Nous savons tous que pour invoquer de l'Objet.wait()
, cet appel doit être placé dans synchronisé bloc, sinon un IllegalMonitorStateException
est levée. Mais quelle est la raison de cette restriction? Je sais que wait()
communiqués de le surveiller, mais pourquoi avons-nous besoin explicitement acquérir le moniteur en faisant notamment le bloc synchronisé, puis relâchez-le moniteur en appelant wait()
?
Quel est le potentiel de dégâts si il était possible d'invoquer wait()
à l'extérieur d'un bloc synchronisé, en conservant c'est de la sémantique - la suspension de l'appelant fil?
Vous devez vous connecter pour publier un commentaire.
Un
wait()
n'a de sens que lorsqu'il y a également unnotify()
, il est donc toujours au sujet de la communication entre les threads, et que les besoins de synchronisation pour fonctionner correctement. On pourrait arguer que ce devrait être implicite, mais qui ne serait pas vraiment aider, pour la raison suivante:Sémantiquement, vous n'avez jamais juste
wait()
. Vous avez besoin d'une condition à satsified, et si c'est non, vous attendez jusqu'à ce qu'il est. Donc, ce que vous avez vraiment à faire est deMais la condition est définie par un thread séparé, afin d'avoir ce travail correctement vous avez besoin de synchronisation.
Quelques choses de mal avec elle, où tout simplement parce que votre fil de quitter l'attente ne veut pas dire la condition que vous êtes à la recherche pour les est vrai:
Vous pouvez obtenir de fausses réveils (ce qui signifie qu'un thread peut réveiller d'attente sans avoir jamais reçu une notification), ou
La condition peut obtenir le jeu, mais un thread troisième sorte que la condition faux à nouveau par le temps le thread en attente se réveille (et rachète le moniteur).
Pour faire face à ces cas, ce que vous avez vraiment besoin est toujours une variante de ceci:
Mieux encore, ne plaisante pas avec les primitives de synchronisation à tous et à travailler avec les abstractions proposées dans le
java.util.concurrent
paquets.Thread.interrupted()
ainsi.notify()
(et donc pas de troisième thread en question). Fausse interruptions sur Wikipedia , lecture Intéressante sur pourquoi ils existentsynchronized
bloc? Je veux dire changer decondition
variable doit être atomique droit?Illustrons les questions que nous serions confrontés à de si
wait()
pourraient être appelées à l'extérieur d'un bloc synchronisé avec un exemple concret.Supposons que nous avons à mettre en œuvre un blocage de la file d'attente (je sais, il y a déjà une dans l'API 🙂
Une première tentative (sans synchronisation) pourrait ressembler à quelque chose le long des lignes ci-dessous
C'est ce qui pourrait éventuellement arriver:
Un thread consommateur appels
take()
et voit que lebuffer.isEmpty()
.Avant que le thread consommateur fait appel
wait()
, un producteur de fil vient le long et appelle un pleingive()
, qui est,buffer.add(data); notify();
Le thread consommateur fera alors appel à
wait()
(et miss lanotify()
qui vient d'être appelé).Si malchanceux, le producteur thread ne produira pas plus
give()
comme un résultat du fait que le thread consommateur ne se réveille jamais, et nous avons une impasse.Une fois que vous comprenez le problème, la solution est évidente: Utiliser
synchronized
pour s'assurernotify
n'est jamais appelé entreisEmpty
etwait
.Sans entrer dans les détails: Ce problème de synchronisation est universelle. Comme Michael Borgwardt souligne, wait/notify est tout au sujet de la communication entre les threads, de sorte que vous aurez toujours une condition de course similaire à celle décrite ci-dessus. C'est pourquoi la "plus qu'à attendre à l'intérieur synchronisée", la règle est appliquée.
Un paragraphe de la lien posté par @Willie résume assez bien:
Le prédicat que le producteur et le consommateur à convenir est dans l'exemple ci-dessus
buffer.isEmpty()
. Et le contrat est résolu en veillant à ce que l'attente et à notifier sont effectuées danssynchronized
blocs.Ce message a été réécrit comme un article ici: Java: Pourquoi attendre doit être appelée dans un bloc synchronisé
return buffer.remove();
en tout en bloc, mais aprèswait();
, ça marche?wait
retourne.Thread.currentThread().wait();
dans lemain
fonction entouré par un try-catch pourInterruptedException
. Sanssynchronized
bloc, il me donne la même exceptionIllegalMonitorStateException
. Ce qui le rend atteindre d'état illégales maintenant? Il fonctionne à l'intérieur desynchronized
bloc bien.Object lock = new Object();
champ dans votre classe, et nelock.wait();
(ou quelque chose de similaire). Je n'ai jamais vu personne d'appelwait()
sur unThread
objet.Thread
objet bu la javamain
fonction. Je comprends que ça n'a pas de sens. Mais pourquoi donne-t-il exception? Ce qui se passe exactement à l'intérieur dewait
?synchronized
fait en sorte de vous-même que moniteur. Vous pouvez uniquement appelerwait
sur les moniteurs que vous possédez. Voir la javadoc pour attendre: "Le thread courant doit être propriétaire de cet objet à l'écran."synchronized
de la méthode/de bloc. Mais au moins, il applique pourwait()
etnotify()
, droit? Est-il une situation où l'état peut être mis à l'extérieursynchronized
et seulementwait
etnotify
à l'intérieur?notify
etcheckCondition&wait
actions, et c'est obtenue par l'exécutionnotify
etcheckCondition&wait
dans synchronisé blocs. Mais il n'y a pas de raison de universellement besoin d'un ordre total entresetConditionToTrue¬ify
etcheckCondition&wait
actions, donc il n'y a pas de raison de universellement besoin de mettresetConditionToTrue
etnotify
dans un bloc synchronisé. Êtes-vous d'accord?@Rollerball est droit. Le
wait()
est appelé, de sorte que le fil peut attendre une condition pour se produire lors de cettewait()
appel arrive, le fil est forcé d'abandonner sa serrure.Renoncer à quelque chose, il faut d'abord. Thread doit posséder la fermeture.
D'où la nécessité d'appeler à l'intérieur d'un
synchronized
de la méthode/de bloc.Oui, je suis d'accord avec toutes les réponses ci-dessus concernant les dommages potentiels/incohérences si vous n'avez pas de vérifier l'état au sein de
synchronized
de la méthode/de bloc. Cependant, comme @shrini1000 a souligné, à juste appelerwait()
dans synchronisé bloc ne sera pas éviter cette incohérence de passe.Voici une belle lecture..
Le problème, il peut causer si vous ne pas synchroniser avant de
wait()
est comme suit:makeChangeOnX()
et vérifie la condition du while, et il esttrue
(x.metCondition()
retournefalse
, signifiex.condition
estfalse
) ainsi il sera à l'intérieur. Puis juste avant lawait()
méthode, un autre thread va àsetConditionToTrue()
et définit lax.condition
àtrue
etnotifyAll()
.wait()
méthode (pas affectée par lanotifyAll()
qui s'est passé quelques instants avant).Dans ce cas, le 1er thread va rester en attente pour un autre thread pour effectuer
setConditionToTrue()
, mais ça pourrait ne pas se produire à nouveau.Nous savons tous que le wait(), notify() et notifyAll() sont les méthodes utilisées pour l'inter-thread
les communications. Pour se débarrasser de manquer de signal et de fausse réveil problèmes, thread en attente
attend toujours en fonction de certaines conditions.
par exemple-
Puis avisant ensembles de fil wasNotified variable à true et informer.
Chaque thread a son cache local donc tous les changements de la première à obtenir écrite, il y a et
ensuite promu à la mémoire principale progressivement.
Eu ces méthodes qui n'ont pas invoqué dans synchronisé bloc, le wasNotified variable
ne serait pas être évacuées dans la mémoire principale et la serait dans le thread du cache local
si le thread en attente gardera attendant le signal du bien qu'il a été réinitialisé par une notification
fil de discussion.
Pour résoudre ces types de problèmes, ces méthodes sont toujours appelées à l'intérieur de bloc synchronisé
qui assure que lors d'une synchronisation de bloc commence alors tout sera lu à partir des principaux
mémoire et sera injectée dans la mémoire principale avant de quitter le bloc synchronisé.
Merci, j'espère qu'elle précise.
directement à partir de cette java oracle tutoriel:
En fait cela a à voir avec l'architecture matérielle (c'est à dire RAM et caches).
Si vous n'utilisez pas
synchronized
avecwait()
ounotify()
, un autre thread pourrait entrer dans le même bloc au lieu d'attendre pour le moniteur d'y entrer. En outre, lors de l'accès à un tableau sans en parallèle, un bloc, un autre thread ne peut pas voir le changement pour elle...en fait un autre thread ne voir toute les changements à quand il a déjà une copie du tableau dans le x-mémoire cache de niveau (un.k.un. 1ère/2ème/3ème niveau caches) de la manipulation du fil de cœur de PROCESSEUR.Mais synchronisé blocs sont d'un seul côté de la médaille: Si vous avez réellement accéder à un objet à l'intérieur d'un synchronisé contexte de non-synchronisé contexte, l'objet continueront de ne pas être synchronisés au sein d'un même bloc synchronisé, car il contient une copie de l'objet dans son cache. J'ai écrit à ce sujet de questions se posent ici: https://stackoverflow.com/a/21462631 et Lorsqu'un verrou est titulaire d'un non-objet final, la référence de l'objet encore être changé par un autre thread?
En outre, je suis convaincu que le x-niveau caches sont responsables pour la plupart des non-reproductible des erreurs d'exécution. C'est parce que les développeurs ne font généralement pas de savoir le faible niveau de stuff, comme l'utilisation de l'UC de travail ou de la façon dont la hiérarchie de mémoire affecte le fonctionnement d'applications: http://en.wikipedia.org/wiki/Memory_hierarchy
Il reste une énigme pourquoi la programmation des classes ne commencent pas avec la hiérarchie de mémoire et de PROCESSEUR d'architecture de la première. "Bonjour le monde" ne va pas aider ici. 😉
wait()
ouwait(integer)
quelque part dans unrun()
méthode. Btw, je suis à l'aide de NetBeans IDE (7.2)...ce qui me permet d'écrire du code plusieurs fois plus rapide que dans Eclipse et sans aucune absurdité de messages d'erreur. 😉 Oh, btw, je dois vous remercier pour vos remarques, parce qu'il m'a gardé à la réflexion sur le trou synchronisé choses une fois de plus et plus en profondeur, et je dois avouer que je que partiellement compris ce qui se passait réellement (je ne dis pas que mon point de vue de la JVM étaient complètement fausses, mais en partie!). Qui enfin...Lorsque vous appelez notify() d'un objet t, java notifie un particulier t.wait() de la méthode. Mais, comment se fait la java de recherche et de notifier un particulier attendre méthode.
java ne regarde dans le synchronisée bloc de code qui a été verrouillé par t objet. java ne permet pas de rechercher l'ensemble du code de notifier un particulier t.wait().
comme par docs:
wait()
méthode signifie simplement qu'il libère le verrou sur l'objet. Si l'objet est verrouillé uniquement dans le bloc synchronisé/méthode. Si le thread est en dehors de la synchronisation de bloc signifie qu'il n'est pas verrouillé, si elle n'est pas verrouillé, que feriez-vous communiqué sur l'objet?