web-dev-qa-db-fra.com

Concept derrière la mise en place de méthodes wait (), notify () dans la classe Object

J'ai juste du mal à comprendre le concept qui sous-tend le fait de placer wait() dans la classe Object. Pour ces questions, considérons si wait() et notifyAll() sont dans la classe Thread.

class Reader extends Thread {
    Calculator c;
    public Reader(Calculator calc) {
        c = calc;
    }

    public void run() {
        synchronized(c) {                              //line 9
        try {
            System.out.println("Waiting for calculation...");
            c.wait();
        } catch (InterruptedException e) {}
            System.out.println("Total is: " + c.total);
        }
    }

    public static void main(String [] args) {
        Calculator calculator = new Calculator();
        new Reader(calculator).start();
        new Reader(calculator).start();
        new Reader(calculator).start();
        calculator.start();
    }
}

class Calculator extends Thread {
    int total;
    public void run() {
        synchronized(this) {                     //Line 31
            for(int i=0;i<100;i++) {
                total += i;
            }
             notifyAll();
        }
    } 
}

Ma question est la suivante: quelle différence cela aurait-il pu faire? Dans la ligne 9, nous acquérons un verrou sur l’objet c, puis nous exécutons une attente qui satisfait à la condition d’attente selon laquelle nous avons besoin d’acquérir un verrou sur l’objet avant de l’utiliser. .

40
Sunny

J'ai juste du mal à comprendre le concept derrière mettre wait () dans la classe d'objet. Pour cette question, considérons comme si wait () et notifyAll () étaient dans la classe thread

En langage Java, vous wait() sur une instance particulière de Object - un moniteur affecté à cet objet pour être précis. Si vous souhaitez envoyer un signal à un thread en attente sur cette instance d'objet spécifique, appelez notify() sur cet objet. Si vous souhaitez envoyer un signal à tous les threads en attente sur cette instance d'objet, vous utilisez notifyAll() sur cet objet.

Si wait() et notify() étaient plutôt à la Thread, chaque thread devrait connaître l'état de tous les autres. Comment thread1 pourrait-il savoir que thread2 attendait l'accès à une ressource particulière? Si thread1 devait appeler thread2.notify(), il devrait en quelque sorte découvrir que thread2 était en attente. Il faudrait qu'il existe un mécanisme permettant aux threads d'enregistrer les ressources ou les actions dont ils ont besoin pour que les autres puissent les signaler lorsque le contenu est prêt ou disponible.

En Java, l'objet lui-même est l'entité partagée entre les threads, ce qui leur permet de communiquer entre eux. Les threads n'ont aucune connaissance spécifique les uns des autres et peuvent s'exécuter de manière asynchrone. Ils courent et ils verrouillent, attendent et notifient sur le objet auquel ils veulent accéder. Ils n'ont aucune connaissance des autres threads et n'ont pas besoin de connaître leur statut. Ils n'ont pas besoin de savoir que c'est thread2 qui attend la ressource. Ils notifient simplement sur la ressource et quiconque l'attend (le cas échéant) en sera informé.

En Java, nous utilisons ensuite des objets de verrouillage comme points de synchronisation, de mutex et de communication entre les threads. Nous synchronisons sur un objet lock pour obtenir un accès mutex à un bloc de code important et pour synchroniser la mémoire. Nous attendons un objet si nous attendons qu'une condition soit changée - une ressource devienne disponible. Nous notifions sur un objet si nous voulons réveiller les threads en veille.

// locks should be final objects so the object instance we are synchronizing on,
// never changes
private final Object lock = new Object();
...
// ensure that the thread has a mutex lock on some key code
synchronized (lock) {
    ...
    // i need to wait for other threads to finish with some resource
    // this releases the lock and waits on the associated monitor
    lock.wait();
    ...
    // i need to signal another thread that some state has changed and they can
    // awake and continue to run
    lock.notify();
}

Votre programme peut contenir n'importe quel nombre d'objets de verrouillage, chacun verrouillant une ressource ou un segment de code particulier. Vous pourriez avoir 100 objets de verrouillage et seulement 4 threads. Lorsque les threads exécutent les différentes parties du programme, ils ont un accès exclusif à l’un des objets de verrouillage. Encore une fois, ils ne doivent pas connaître l'état d'exécution des autres threads.

Cela vous permet d’augmenter ou réduire le nombre de threads exécutés dans votre logiciel autant que vous le souhaitez. Vous trouvez que les 4 threads bloquent trop sur les ressources extérieures, alors vous pouvez augmenter le nombre. Si vous poussez trop fort votre serveur détruit, réduisez le nombre de threads en cours d'exécution. Les objets de verrouillage assurent une communication mutex et mutex indépendante du nombre de threads en cours d'exécution.

92
Gray

Pour mieux comprendre pourquoi les méthodes wait () et notify () appartiennent à la classe Object, je vais vous donner un exemple concret: Supposons qu'une station-service ait une seule toilette, dont la clé est conservée au centre de services. Les toilettes sont une ressource commune pour les automobilistes de passage. Pour utiliser cette ressource partagée, l’utilisateur potentiel doit acquérir une clé pour le verrou des toilettes. L'utilisateur se rend au centre de services et acquiert la clé, ouvre la porte, la verrouille de l'intérieur et utilise les installations.

En attendant, si un deuxième utilisateur potentiel arrive à la station-service, il trouve les toilettes verrouillées et par conséquent inaccessibles. Il se rend au centre de services mais la clé n’est pas là car elle est entre les mains de l’utilisateur actuel. Lorsque l'utilisateur actuel a terminé, il déverrouille la porte et renvoie la clé au centre de services. Il ne se soucie pas d'attendre les clients. Le centre de services donne la clé au client en attente. Si plusieurs utilisateurs potentiels se présentent alors que la toilette est verrouillée, ils doivent former une file d'attente en attendant la clé de la serrure. Chaque fil n'a aucune idée de qui est dans les toilettes.

Évidemment, en appliquant cette analogie à Java, un thread Java est un utilisateur et la toilette est un bloc de code que le thread souhaite exécuter. Java fournit un moyen de verrouiller le code d'un thread qui l'exécute actuellement à l'aide du mot clé synchronized, et de faire attendre les autres threads souhaitant l'utiliser jusqu'à la fin du premier thread. Ces autres threads sont placés dans l'état d'attente. Java n'est pas aussi juste que la station-service car il n'y a aucune file d'attente pour les threads en attente. N'importe lequel des threads en attente peut recevoir le moniteur ensuite, quel que soit l'ordre dans lequel ils l'ont demandé. La seule garantie est que tous les threads utiliseront le code surveillé tôt ou tard.

Enfin, la réponse à votre question: la serrure pourrait être l’objet clé ou le centre de services. Aucun de ce qui est un fil.

Cependant, ce sont les objets qui décident actuellement si les toilettes sont verrouillées ou ouvertes. Ce sont les objets qui sont en mesure de signaler que la salle de bain est ouverte («avertir») ou de demander aux gens d’attendre quand elle est verrouillée.

35
Roushan

Les autres réponses à cette question passent toutes à côté du point essentiel: en Java, il existe un mutex associé à every object. (Je suppose que vous savez ce qu'est un mutex ou un "verrou".) C'est pas le cas dans la plupart des langages de programmation qui ont le concept de "verrous". Par exemple, en Ruby, vous devez explicitement créer autant d'objets Mutex que vous avez besoin.

Je pense savoir pourquoi les créateurs de Java ont fait ce choix (même si, à mon avis, c'était une erreur). La raison est liée à l'inclusion du mot clé synchronized. Je crois que les créateurs de Java (naïvement) pensaient qu'en incluant des méthodes synchronized dans le langage, il deviendrait facile pour les gens d'écrire du code multithread correct - encapsulez simplement tout votre état partagé en objets synchronized, et vous avez terminé! Mais ça n'a pas marché comme ça ...

Quoi qu'il en soit, comme toute classe peut avoir des méthodes synchronized, il doit y avoir un mutex pour chaque objet, que les méthodes synchronized peuvent verrouiller et déverrouiller.

wait et notify reposent tous deux sur des mutex. Peut-être que vous comprenez déjà pourquoi c'est le cas ... sinon je peux ajouter plus d'explications, mais pour l'instant, disons simplement que les deux méthodes doivent fonctionner sur un mutex. Chaque objet Java a un mutex, il est donc logique que wait et notify puissent être appelés sur n’importe quel objet Java. Ce qui signifie qu'ils doivent être déclarés en tant que méthodes de Object.

Une autre option aurait été de mettre des méthodes statiques sur Thread ou quelque chose qui prendrait n'importe quelle Object en argument. Cela aurait été beaucoup moins déroutant pour les nouveaux programmeurs Java. Mais ils ne l'ont pas fait de cette façon. Il est beaucoup trop tard pour changer ces décisions. dommage!

5
Alex D

La réponse à votre première question est la suivante: chaque objet Java n’ayant qu’un lock(monitor) et un wait(),notify(),notifyAll() sont utilisés pour le partage de contrôle, c’est pourquoi ils font partie de la classe Object au lieu de Threadclass.

4
amicngh

En termes simples, les raisons sont les suivantes.

  1. Object a des moniteurs.
  2. Plusieurs threads peuvent accéder à une Object. Un seul thread peut contenir le moniteur d'objet à la fois pour __ méthodes/blocs synchronized.
  3. La méthode wait(), notify() and notifyAll() étant dans la classe Object, tous les threads créés sur cette object peuvent communiquer avec d’autres
  4. Le verrouillage (à l'aide de l'API synchronized or Lock) et la Communication (wait() and notify()) sont deux concepts différents. 

Si la classe Thread contient des méthodes wait(), notify() and notifyAll(), les problèmes suivants seront créés:

  1. Thread problème de communication
  2. Synchronization sur objet ne sera pas possible. Si chaque thread a un moniteur, nous n’aurons aucun moyen de réaliser la synchronisation.
  3. Inconsistency en état d'objet 

Reportez-vous à cet article pour plus de détails. 

1
Ravindra babu

les opérations wait and notify fonctionnent sur un verrou implicite, et le verrouillage implicite rend possible la communication entre threads. Et tous les objets ont leur propre copie d'objet implicite. il est donc judicieux d’attendre et d’indiquer où le verrouillage implicite est la vie. 

Autrement, patientez et informez que vous auriez peut-être vécu dans la classe Thread. Au lieu de wait (), il se peut que nous devions appeler Thread.getCurrentThread (). wait (), de même que notify. Pour les opérations wait et notify, il existe deux paramètres obligatoires: l'un est le thread qui attend ou notifie l'autre le verrouillage implicite de l'objet. les deux sont ceux-ci pourraient être disponibles à la fois dans Object et dans la classe de thread. La méthode wait () dans la classe Thread aurait fait la même chose que dans la classe Object: le thread en cours de transition passe en attente en attente du verrou acquis en dernier.

Donc oui, je pense que wait and notify aurait pu être là aussi dans la classe Thread, mais cela ressemble plus à une décision de conception de le garder dans la classe d'objet.

0
leoismyname

Ces méthodes fonctionnent sur les verrous et les verrous sont associés à Object et non à Threads. Par conséquent, il est dans la classe Object.

Les méthodes wait (), notify () et notifyAll () ne sont pas simplement des méthodes, ce sont des utilitaires de synchronisation et sont utilisées dans le mécanisme de communication entre les threads en Java.

Pour une explication plus détaillée, rendez-vous sur: http://parameshk.blogspot.in/2013/11/why-wait-notify-and-notifyall-methods.html

0
Paramesh Korrakuti

Ceci est juste mes 2 cents sur cette question ... pas sûr si cela est vrai dans son intégralité. 

Chaque objet a un moniteur et un waitet -> ensemble de threads (probablement au niveau du système d'exploitation). Cela signifie que le moniteur et le serveur d'attente peuvent être considérés comme des membres privés d'un objet. Avoir les méthodes wait () et notify () dans la classe Thread impliquerait de donner un accès public au bloc d’attente ou d’utiliser des méthodes get-set pour le modifier. Vous ne voudriez pas faire cela parce que c'est une mauvaise conception.

Maintenant, étant donné que l’objet connaît le ou les threads en attente de son moniteur, cela devrait être le travail de l’objet d’aller réveiller les threads qui l’attendent plutôt possible que si l’objet de classe de thread est autorisé à accéder au bloc d’attente). Cependant, ce n'est pas le travail d'un thread particulier d'aller réveiller chacun des threads en attente. (C'est exactement ce qui se passerait si toutes ces méthodes étaient dans la classe Thread). Son travail consiste simplement à libérer le verrou et à aller de l'avant avec sa propre tâche. Un thread fonctionne de manière indépendante et n'a pas besoin de savoir quels autres threads attendent le moniteur d'objets (il s'agit d'un détail inutile pour l'objet de classe de thread). S'il a commencé à éveiller chaque thread par lui-même, il s'éloigne de ses fonctionnalités principales et remplit sa propre tâche. Lorsque vous pensez à une scène où il pourrait y avoir des milliers de discussions, vous pouvez supposer l’impact que cela peut avoir sur les performances. Par conséquent, étant donné que Object Class sait qui l'attend, il peut effectuer le travail qui réveille les threads en attente et le thread qui a envoyé notify () peut effectuer avec son traitement ultérieur. 

Pour donner une analogie (peut-être pas la bonne mais ne peut penser à rien d’autre). En cas de panne de courant, nous appelons une représentante de cette société car elle connaît les bonnes personnes à contacter pour la réparer. En tant que consommateur, vous n'êtes pas autorisé à savoir qui sont les ingénieurs derrière cela et même si vous le savez, vous ne pouvez éventuellement pas appeler chacun d'eux et leur parler de vos ennuis (ce n'est pas votre devoir. panne et le travail du CR est d’aller en informer (réveiller) les bons ingénieurs pour le faire).

Faites-moi savoir si cela sonne juste ... (j'ai la capacité de confondre parfois avec mes mots).

0
BHS

wait - La méthode wait indique au thread actuel d'abandonner le moniteur et de s'endormir.

notify - Réveille un seul thread en attente sur le moniteur de cet objet.

Ainsi, vous voyez que les méthodes wait () et notify () fonctionnent au niveau du moniteur, le thread qui détient actuellement le moniteur est invité à abandonner ce moniteur via la méthode wait () et les threads de méthode notify (ou notifyAll) en attente du le moniteur de l'objet est averti que les threads peuvent se réveiller.

Un point important à noter ici est que le moniteur est affecté à un objet et non à un thread particulier. C'est l'une des raisons pour lesquelles ces méthodes sont dans la classe Object . Pour réitérer les threads, attendez sur le moniteur d'un objet (lock) et notify () est également appelé sur un objet pour réactiver un thread en attente sur le moniteur de l'objet.

0
Ankur Kunal