Pourquoi tous les objets Java ont-ils wait () et notify () et cela cause-t-il un impact négatif sur les performances?

StackOverflow https://stackoverflow.com/questions/1648551

  •  22-07-2019
  •  | 
  •  

Question

Chaque Java Object utilise les méthodes wait() et notify() (ainsi que des variantes supplémentaires). Je ne les ai jamais utilisées et je soupçonne que beaucoup d'autres ne les ont pas utilisées. Pourquoi ceux-ci sont-ils si fondamentaux que chaque objet doit les avoir et y at-il un impact négatif sur leur performance (vraisemblablement, un état est stocké dans ces objets)?

MODIFIER pour souligner la question. Si j’ai un List<Double> avec 100 000 éléments, alors chaque Double utilise ces méthodes car elle est étendue à partir de List. Mais il semble peu probable que tous ces éléments doivent connaître les threads qui gèrent le <=>.

MODIFIER des réponses excellentes et utiles. @Jon a un très bon billet de blog qui a cristallisé mes sentiments. Je suis également entièrement d'accord avec @Bob_Cross sur le fait que vous devriez montrer un problème de performance avant de vous en préoccuper. (Également comme la nième loi des langues à succès si cela avait été un succès de performance, alors Sun ou quelqu'un l'aurait corrigé).

Était-ce utile?

La solution

Cela signifie que chaque objet doit potentiellement être associé à un moniteur. Le même moniteur est utilisé pour synchronized. Si vous êtes d'accord avec la décision de pouvoir synchroniser n'importe quel objet, alors wait() et notify() n'ajoutez plus d'état par objet. La machine virtuelle Java peut allouer le moniteur réel paresseusement (je sais que .NET le fait), mais un espace de stockage doit être disponible pour indiquer quel moniteur est associé à l'objet. Certes, il est possible qu'il s'agisse d'une très petite quantité (par exemple, 3 octets) qui ne sauverait en réalité aucune mémoire en raison du bourrage du reste de la charge de l'objet. Il vous faudrait regarder comment chaque machine virtuelle Java gère la mémoire pour dire à coup sûr.

Notez que le simple fait de disposer de méthodes supplémentaires n'affecte pas les performances (très légèrement, en raison de la présence évidente du code quelque part ). Ce n'est pas comme si chaque objet ou même chaque type avait sa propre copie du code pour <=> et <=>. Selon le fonctionnement des tables vtables, chaque type peut se voir attribuer une entrée vtable supplémentaire pour chaque méthode héritée - mais cela ne concerne toujours que les types et non les objets. Cela va se perdre dans le bruit par rapport à la majeure partie de la mémoire qui est destinée aux objets eux-mêmes.

Personnellement, j’ai le sentiment que .NET et Java ont commis une erreur en associant un moniteur à chaque objet. Je préférerais plutôt avoir des objets de synchronisation explicites. J'ai écrit un peu plus à ce sujet dans un article de blog sur la refonte de java.lang.Object / System.Object .

Autres conseils

  

Pourquoi sont-ils si fondamentaux que   chaque objet doit les avoir et est   il y a une performance frappé en les ayant   (vraisemblablement, un état est stocké dans   eux)?

tl; dr: ce sont des méthodes de sécurité des threads et leurs coûts sont minimes par rapport à leur valeur.

Les réalités fondamentales que Ces méthodes sont les suivantes:

  1. Java est toujours multi-threadé. Exemple: consultez la liste des threads utilisés par un processus utilisant jconsole ou jvisualvm quelque temps.
  2. La correction est plus importante que & la performance. & la; Lorsque je notais des projets (il y a de nombreuses années), j'avais l'habitude d'expliquer & "Se tromper de réponse très vite est toujours faux. &";

Fondamentalement, ces méthodes fournissent certains des points d'ancrage permettant de gérer les moniteurs par objet utilisés lors de la synchronisation. Plus précisément, si synchronized(objectWithMonitor) j'ai une méthode particulière, je peux utiliser objectWithMonitor.wait() pour générer ce moniteur (par exemple, si j'ai besoin d'une autre méthode pour effectuer un calcul avant de pouvoir continuer). Dans ce cas, cela permettra à une autre méthode bloquée d’attendre que ce moniteur continue.

D'autre part, je peux utiliser objectWithMonitor.notifyAll() pour que les threads en attente du moniteur sachent que je vais bientôt abandonner le moniteur. Cependant, ils ne peuvent pas continuer tant que je n'ai pas quitté le bloc synchronisé.

En ce qui concerne les exemples spécifiques (par exemple, les longues listes de doublons) dans lesquels vous pourriez craindre des problèmes de performance ou de mémoire sur le mécanisme de contrôle, voici quelques points à prendre en compte:

  1. Tout d'abord, prouvez-le. Si vous pensez qu'un mécanisme Java de base, tel que la correction multi-thread, a un impact majeur, il y a de bonnes chances que votre intuition soit fausse. Mesurer l'impact en premier. Si le problème est grave et que vous savez que vous n’aurez jamais besoin de synchroniser sur un double individuel, envisagez plutôt d’utiliser des doubles.
  2. Si vous n'êtes pas certain que vous, votre collègue, un futur codeur de maintenance (qui pourrait être vous-même un an plus tard), etc., vous n'aurez jamais besoin d'une finesse de détail En accédant plus facilement à vos données, il est fort probable que le retrait de ces moniteurs rendrait votre code moins souple et moins facile à gérer.

Suivi en réponse à la question sur les objets de surveillance par objet vs explicites:

Réponse courte: @JonSkeet: oui, la suppression des moniteurs créerait des problèmes: cela créerait des frictions. Garder ces moniteurs dans Object nous rappelle que c'est toujours un système multithread.

Les analyses d'objet intégrées ne sont pas sophistiquées, mais elles sont: faciles à expliquer; travailler de manière prévisible; et sont clairs dans leur but. synchronized(this) est une déclaration d’intention claire. Si nous forçons les codeurs débutants à utiliser exclusivement le package de concurrence, nous introduisons des frictions. Qu'y a-t-il dans ce paquet? Qu'est-ce qu'un sémaphore? Joindre à la fourche?

Un codeur débutant peut utiliser les moniteurs Object pour écrire du code de modèle-vue-contrôleur décent. synchronized, wait et notifyAll peuvent être utilisés pour mettre en œuvre une sécurité des threads naïve (au sens de performance simple, accessible mais peut-être pas exagérée). L'exemple canonique serait l'un de ces doublons (posés par l'OP) qui peut avoir un thread qui définit une valeur alors que le thread AWT obtient la valeur pour le placer sur un JLabel. Dans ce cas, il n'y a aucune bonne raison de créer un objet supplémentaire explicite simplement pour avoir un moniteur externe.

À un niveau de complexité légèrement supérieur, ces mêmes méthodes sont utiles en tant que méthode de surveillance externe. Dans l'exemple ci-dessus, je l'ai explicitement fait (voir les fragments objectWithMonitor ci-dessus). Encore une fois, ces méthodes sont vraiment pratiques pour mettre en place une sécurité de fil relativement simple.

Si ouiSi vous souhaitez être encore plus sophistiqué, pensez à lire Concurrence Java en pratique (si vous ne l'avez pas déjà) Les verrous en lecture et en écriture sont très puissants sans ajouter de complexité supplémentaire.

Ligne de frappe : à l'aide de méthodes de synchronisation de base, vous pouvez exploiter une grande partie des performances activées par les processeurs multicœurs modernes avec une sécurité des threads et sans surcharge.

Tous les objets en Java sont associés à des moniteurs. Les primitives de synchronisation sont utiles dans à peu près tout le code multi-thread, et sa très sémantique est très agréable à synchroniser sur le ou les objets auxquels vous accédez plutôt que sur un & "Monitor &" Séparé; objets.

Java peut allouer les moniteurs associés aux objets selon les besoins - comme le fait le .NET - et dans tous les cas, la surcharge réelle due à la simple affectation (mais pas à l'utilisation) du verrou serait assez petite.

En bref: il est très pratique de stocker des objets avec leurs éléments de support de sécurité des threads, et l'impact sur les performances est très réduit.

Ces méthodes permettent d'implémenter la communication inter-thread.

Consultez cet article sur le sujet .

Règles pour ces méthodes, extraites de cet article:

  • wait () indique au fil d’appel d’abandonner le moniteur et de s’endormir jusqu’à ce qu’un autre   Le thread entre dans le même moniteur et appelle notify ().
  • notify () réveille le premier thread qui a appelé wait () sur le même objet.
  • notifyAll () réveille tous les threads qui ont appelé wait () sur le même objet. le   le thread le plus prioritaire sera exécuté en premier.

J'espère que cela vous aidera ...

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