Question

Quelqu'un au travail vient de demander le raisonnement qui sous-tend l'obligation de placer une attente dans une synchronisation.

Honnêtement, je ne vois pas le raisonnement. Je comprends ce que disent les javadocs - que le fil doit être le propriétaire du moniteur de l'objet, mais pourquoi? Quels problèmes empêche-t-il? (Et si c'est réellement nécessaire, pourquoi la méthode d'attente ne peut-elle pas obtenir le moniteur lui-même?)

Je cherche un pourquoi assez détaillé ou peut-être une référence à un article. Je n'ai pas pu en trouver un dans un rapide google.

Oh, aussi, comment se compare thread.sleep?

modifier: Excellent ensemble de réponses - j'aimerais vraiment pouvoir en sélectionner plusieurs, car elles m'ont toutes aidé à comprendre ce qui se passait.

Était-ce utile?

La solution

Si l'objet ne possède pas le moniteur d'objet lorsqu'il appelle Object.wait (), il ne pourra pas accéder à l'objet pour configurer un écouteur de notification jusqu'à ce que le moniteur soit libéré. Au lieu de cela, il sera traité comme un thread essayant d'accéder à une méthode sur un objet synchronisé.

Ou, autrement dit, il n'y a pas de différence entre:

public void doStuffOnThisObject()

et la méthode suivante:

public void wait()

Les deux méthodes seront bloquées jusqu'à ce que le moniteur d'objets soit libéré. Il s'agit d'une fonctionnalité Java qui empêche l'état d'un objet d'être mis à jour par plusieurs threads. Cela a simplement des conséquences inattendues sur la méthode wait ().

Vraisemblablement, la méthode wait () n'est pas synchronisée car cela pourrait créer des situations où le thread a plusieurs verrous sur l'objet. (Voir Spécifications / verrouillage du langage Java pour obtenir des informations complémentaires. plus d'informations à ce sujet.) Plusieurs verrous sont un problème parce que la méthode wait () annulera qu'un verrou. Si la méthode était synchronisée, cela garantirait que seul le verrou de la méthode serait annulé tout en laissant un éventuel verrou externe annulé. Cela créerait une impasse dans le code.

Pour répondre à votre question sur Thread.sleep (), Thread.sleep () ne garantit pas que la condition que vous attendez a été remplie. L'utilisation de Object.wait () et Object.notify () permet à un programmeur d'implémenter manuellement le blocage. Les threads seront débloqués une fois qu'une notification est envoyée indiquant qu'une condition a été remplie. par exemple. Une lecture du disque est terminée et les données peuvent être traitées par le thread. Thread.sleep () obligerait le programmeur à interroger si la condition est remplie, puis à s'endormir s'il ne le fait pas.

Autres conseils

Beaucoup de bonnes réponses ici déjà. Mais je tiens simplement à mentionner ici que l’autre MUST À FAIRE lors de l’utilisation de wait () est de le faire en boucle, en fonction de la condition que vous attendez au cas où vous verriez des wakeups parasites, ce qui, selon mon expérience, se produit.

Pour attendre qu'un autre thread modifie une condition en true et le notifie:

synchronized(o) {
  while(! checkCondition()) {
    o.wait();
  }
}

Bien sûr, ces jours-ci, je recommanderais simplement d'utiliser le nouvel objet Condition, car il est plus clair et offre davantage de fonctionnalités (comme permettre plusieurs conditions par verrou, pouvoir vérifier la longueur de la file d'attente, une planification / interruption plus flexible, etc. ).

 Lock lock = new ReentrantLock();
 Condition condition = lock.newCondition();
 lock.lock();
 try {
   while (! checkCondition()) {
     condition.await();
   }
 } finally {
   lock.unlock();
 }

}

Il doit posséder le moniteur, car le but de wait () est de libérer le moniteur et de laisser les autres threads obtenir le moniteur pour effectuer leur propre traitement. Le but de ces méthodes (wait / notify) est de coordonner l’accès aux blocs de code synchronisés entre deux threads qui nécessitent une fonctionnalité donnée. Il ne s'agit pas simplement de s'assurer que l'accès à une structure de données est threadsafe, mais de coordonner les événements entre plusieurs threads.

Un exemple classique serait un cas producteur / consommateur dans lequel un thread pousse les données vers une file d'attente et un autre thread consomme les données. Le thread consommateur aurait toujours besoin du moniteur pour accéder à la file d'attente, mais le libérerait une fois que la file d'attente est vide. Le thread producteur n'aurait alors accès à l'écriture que lorsque le consommateur ne traiterait plus. Il avertira le thread consommateur une fois qu'il aura introduit plus de données dans la file d'attente, afin qu'il puisse récupérer le moniteur et accéder à nouveau à la file d'attente.

Wait abandonne le moniteur, vous devez donc l’avoir pour le faire. Notify doit également disposer du moniteur.

La raison principale pour laquelle vous souhaitez effectuer cette opération est de vous assurer que vous disposez du moniteur lorsque vous revenez de wait (). En général, vous utilisez le protocole wait / notify pour protéger certaines ressources partagées et vous le souhaitez. soyez prudent de le toucher lorsque l'attente revient. Idem avec notify - en général, vous modifiez quelque chose, puis appelez notify () - vous souhaitez que le moniteur, apporter des modifications et appeler notify ().

Si vous avez créé une fonction comme celle-ci:

public void synchWait() {
   syncronized { wait(); }
}

Vous n'aurez pas le moniteur une fois l'attente renvoyée - vous pourriez l'obtenir, mais vous pourriez ne pas l'obtenir par la suite.

Je comprends pourquoi la restriction est en fait une exigence. Je me base sur une implémentation de moniteur C ++ que j'ai créée il y a un moment en combinant un mutex et une variable de condition.

Dans un système mutex + condition_variable = monitor , le wait call place la variable de condition dans un état d'attente et libère le mutex. La variable de condition est à l'état partagé. Elle doit donc être verrouillée pour éviter les situations de concurrence entre les threads qui souhaitent attendre et les threads qui souhaitent notifier. Au lieu d'introduire un autre mutex pour verrouiller son état, le mutex existant est utilisé. En Java, le mutex est correctement verrouillé lorsque le thread sur le point d’attendre possède le moniteur.

La plupart du temps, l'attente est terminée s'il existe une condition indiquant qu'une file d'attente est vide.

If(queue is empty)
     queue.wait();

Supposons que la file d'attente est vide. Au cas où le thread en cours préempte après la vérification de la file, alors si un autre le thread ajoute quelques éléments à la file d'attente, le thread actuel ne le saura pas et attendra Etat. C'est faux. Nous devrions donc avoir quelque chose comme

Synchornized(queue)
{
   if(queue is empty)
          queue.wait();
}

Voyons maintenant s’ils s’attendaient à être synchronisés. Comme déjà mentionné dans l'un des commentaires, il ne libère qu'un verrou. Cela signifie que si wait () était synchronisé dans le code ci-dessus, un seul verrou aurait été libéré. Cela implique que le thread en cours attendra avec le verrou pour la file d'attente.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top