IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

avec Java Discussion :

Problème wait et notifyAll


Sujet :

avec Java

  1. #1
    Membre du Club
    Homme Profil pro
    Inscrit en
    Février 2013
    Messages
    95
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2013
    Messages : 95
    Points : 41
    Points
    41
    Par défaut Problème wait et notifyAll
    Bonjour, je commence les thread en Java et je bloque sur un problème entre wait et notifyAll(). Mon programme possède un wait() qui attend dans une boucle un notifyAll() mais lorsque le notifyAll s'active les autres thread rèste bloquer sur le wait()...

    Merci d'avance

  2. #2
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    Salut,

    L'appel de notifyAll() sur une instance envoit un signal sur cette instance : tous les threads qui sont en attente dans un wait() sur cette même instance reçoive le signal, et arrête d'attendre. Dans ton code, le thread fait un wait sur lui-même, dans une boucle, et après la fin de la boucle, se notifie lui-même : autant dire qu'il rentre dans la première itération et n'en sort jamais, mais quand bien même, il se notifirait lui-même, ce qui ne servirait à rien, puisqu'il n'est plus en attente.

    En fait, il faut que l'instance sur laquelle tu fais le wait soit une seule instance commune , accessible par les autres threads, pour qu'ils puissent appeler notify() sur cette instance ( si notifyAll() est appelé, tous les threads en wait sur l'instance se "réveillent").
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  3. #3
    Membre du Club
    Homme Profil pro
    Inscrit en
    Février 2013
    Messages
    95
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2013
    Messages : 95
    Points : 41
    Points
    41
    Par défaut
    Salut et merci

    excuse moi j'ai du mal à comprendre, tu veux dire que je ne peux pas faire de notifyAll() dans la même instance courante ?
    J'aimerais synchroniser mes thread et je ne possède que x fois ce thread qui sont tous indépendant et doivent s'appeler en boucle dans un certain ordre

    Edit: est-il possible de notifier 1 seul thread précisément (avec notify) sans que cela ne soit aléatoire ou fixer sur la première instance..?

  4. #4
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    Ce n'est pas un thread que l'on notifie, mais un sémaphore. En faisaint this.notifyAll et this.wait, tu utilise le threadCourant comme sémaphore. C'est bien, mais comme les autres threads s'utilisent eux même comme sémaphore, ça ne risque pas d'aller bien loin. T'as plein de sémaphores différents et pas deux threads qui utilisent le même.


    Avec notify, tu ne peux pas choisir le thread qui se réveille. Ca peut se mettre en place de manière détournée: mettre dans une variable commune le nom du thread qui dois travailler, tous les autres se rendormant immédiatement. Mais du coup, il faut synchroniser aussi l'appel à cette variable et si tu ne comprends pas déjà les base de wait/notify, tu codera ça n'importe comment et ce sera la catastrophe


    Par contre, comme chaque thread fait un wait() sur lui même (this.wait()) dans ton cas, il suffit que tu fasse un leThreadAReveiller.notify().

  5. #5
    Membre du Club
    Homme Profil pro
    Inscrit en
    Février 2013
    Messages
    95
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2013
    Messages : 95
    Points : 41
    Points
    41
    Par défaut
    Salut tchize et merci de ta réponse.

    Il faudrait donc que chaque thread ai la liste de tout les autres thread ?
    par exemple public static Thread listeThread[nbThread] avec le tableau de thread du main

    et donc que je face :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
       private synchronized void donneLaMain() {
    	   tour = (tour%nbThreads)+1;
               listeThread[tour].notify();
       }

    Est-ce que j'ai bien comprit?

  6. #6
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    C'est ça l'idée pour la sélection du thread, mais il faut synchroniser sur l'instance que tu notifies (comme il faut synchroniser sur l'instance sur laquelle on appelle wait() le cas échéant).

    soit

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    private synchronized void donneLaMain() {
       tour = (tour%nbThreads)+1;
       synchronized ( listeThread[tour] ) {
            listeThread[tour].notify();
       }
    }
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  7. #7
    Membre du Club
    Homme Profil pro
    Inscrit en
    Février 2013
    Messages
    95
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2013
    Messages : 95
    Points : 41
    Points
    41
    Par défaut
    J'ai modifier le code mais j'ai toujours le même résultat.

  8. #8
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    C'est normal, tu es dans le même enchaînement que je décrivais dans mon premier post : chaque thread entre dans la première itération, et s'arrête, et attend, indéfiniment. La seule différence, c'est que si un thread s'arrêtait d'attendre, il notifierait bien le thread suivant (et non plus lui-même). Il manque donc juste la première notification, pour que le premier thread arrête d'attendre, ce qui peut être fait par le main thread.
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  9. #9
    Membre du Club
    Homme Profil pro
    Inscrit en
    Février 2013
    Messages
    95
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2013
    Messages : 95
    Points : 41
    Points
    41
    Par défaut
    J'ai une erreur qui s'ajoute à mon output (l'output est le même que avant:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    thread 2
    thread 3
    Thread 1 ligne 1
    thread 4
    thread 5
    thread 1
    java.lang.IllegalMonitorStateException
    	at java.lang.Object.notify(Native Method)
    	at threadLaboOS3.MainThread.main(MainThread.java:27)

  10. #10
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    C'est parce qu'il manque la synchronisation autour de l'appel du notify().

    Un thread ne peut appeler wait() ou notify() sur une instance que s'il possède un verrou de synchronisation sur cette instance. Donc, on doit toujours avoir ce genre de structure :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    synchronized ( instance ) {
        instance.notify();
    }
    De même pour le wait :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    synchronized ( instance ) {
         instance.wait();
    }
    Tu n'as pas été obligé de faire exactement ça pour le wait, parce que quand tu mets synchronized sur la méthode, ça équivaut à faire un block synchronisé sur l'instance de la classe qui possède la méthode.
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  11. #11
    Membre du Club
    Homme Profil pro
    Inscrit en
    Février 2013
    Messages
    95
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2013
    Messages : 95
    Points : 41
    Points
    41
    Par défaut
    Je n'ai plus d'erreur mais toujours le même output..
    Pourtant j'ai modifier les 2 morceaux de code

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    synchronized (this) {
    	 this.wait();
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    synchronized ( listeThreadR[0] ) {
    	listeThreadR[0].notify();
    }

    P.S existe t'il un pattern facilitant l'utilisant l'utilisation des threads en Java?

  12. #12
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    1) J'avais pas fait attention à ta construction de Thread : c'est l'instance de ThreadRunnable sur laquelle tu fais le wait, alors que c'est sur l'instance de Thread surl aquelle tu fais le notify

    fait un tableau de ThreadRunnable (d'ailleurs tu n'as pas besoin de 2 boucles) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    int nbThread = Integer.parseInt(args[0]);
    				ThreadRunnable.nbThreads = nbThread;
    				ThreadRunnable.tour = 0;
    				ThreadRunnable.listeThread = new ThreadRunnable[nbThread];
     
    				for(int i=0;i<nbThread;i++){
    					ThreadRunnable.listeThread[i]=new ThreadRunnable(i+1);
    					new Thread(ThreadRunnable.listeThread[i]).start();
    				}
     
    				ThreadRunnable.tour = 1;
    				synchronized ( ThreadRunnable.listeThread[0] ) {
    					ThreadRunnable.listeThread[0].notify();
    				}
    2) J'avais pas fait attention à ta condition de boucle :

    Pour le premier thread d'id 1, lors du premier tour, il ne se met pas en état wait, donc il passe directement à la notification, probablement avant que le thread 2 ne soit démarré.

    Donc il faut que tu initialises tour à 0, que tu démarres, et ensuite, avant de notifier le 1er thread pour démarrer le jeu, tu passes à tour à 1 (en fait, il faut simuler à un donneLaMain() au premier thread.

    Et il faut aussi que tour soit volatile, car modifé et lu par plusieurs threads différents.

    3) il y a aussi une erreur dans l'utilisation de tour pour faire le notify : dans le tableau, les indices vont de 0 à n-1, alors que les tours vont de 1 à n...


    Non, il n'y a pas de pattern. Il y a des choses comme les executors, qui permettent des lancer des tâches, avec des pools de threads, et des files d'attentes. Et le SwingWorker dédié au processus long dans l'UI.

    Mais je pense que ce qui complique ton programme c'est la façon de gérer les tours avec la boucle while(tour != myID).

    Regarde cette solution de principe (la seule boucle, c'est pour les tours) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    public class DemoWaitNotify extends Thread {
     
    	private final static List<DemoWaitNotify> threads = new ArrayList<>();
     
    	private final int id;
     
    	private static final boolean SLEEP = false; // mettre true pour faire une
    												// pause entre chaque traitement
     
    	private static volatile int tour;
     
    	public DemoWaitNotify(int id) {
    		super(String.format("Thread %d", id));
    		this.id = id;
    	}
     
    	@Override
    	public void run() {
     
    		while (tour < 10) {
    			System.out.printf("Début attente sémaphore %s%n", getName());
    			synchronized (this) {
    				try {
    					this.wait();
    				} catch (InterruptedException e) {
    				}
    			}
    			System.out.printf("Fin attente sémaphore %s%n", getName());
    			if (SLEEP) {
    				System.out.printf("Thread %s attend une seconde...%n",
    						getName());
    				try {
    					Thread.sleep(1000);
    				} catch (InterruptedException e) {
    				}
    			}
     
    			int nextThread;
     
    			if (id + 1 < threads.size()) {
    				nextThread = id + 1;
    			} else {
    				tour++;
    				System.out.printf("========> Tour %d%n", tour);
    				nextThread = 0;
    			}
     
    			synchronized (threads) {
     
    				DemoWaitNotify thread = threads.get(nextThread);
    				synchronized (thread) {
    					System.out.printf(
    							"%s dit à au thread %s de ne plus attendre...%n",
    							getName(), thread.getName());
    					thread.notify();
    				}
     
    			}
    		}
     
    	}
     
    	public static void main(String[] args) {
     
    		tour = 0;
     
    		System.out.println("Thread main démarre 10 threads...");
    		for (int i = 0; i < 10; i++) {
    			DemoWaitNotify thread = new DemoWaitNotify(i);
    			threads.add(thread);
    		}
     
    		synchronized (threads) {
    			for (DemoWaitNotify thread : threads) {
    				thread.start();
    			}
    		}
     
    		System.out.println("Thread main attend une seconde...");
    		try {
    			Thread.sleep(1000);
    		} catch (InterruptedException e) {
    		}
     
    		synchronized (threads) {
    			System.out.printf("========> Tour %d%n", tour);
    			DemoWaitNotify thread = threads.get(0);
    			System.out.printf("Thread main réveille le thread %s%n",
    					thread.getName());
    			synchronized (thread) {
    				thread.notify(); // réveil le premier thread
    			}
    		}
     
    	}
     
    }
    L'expression "ça marche pas" ne veut rien dire. Indiquez l'erreur, et/ou les comportements attendus et obtenus, et donnez un Exemple Complet Minimal qui permet de reproduire le problème.
    La plupart des réponses à vos questions sont déjà dans les FAQs ou les Tutoriels, ou peut-être dans une autre discussion : utilisez la recherche interne.
    Des questions sur Java : consultez le Forum Java. Des questions sur l'EDI Eclipse ou la plateforme Eclipse RCP : consultez le Forum Eclipse.
    Une question correctement posée et rédigée et vous aurez plus de chances de réponses adaptées et rapides.
    N'oubliez pas de mettre vos extraits de code entre balises CODE (Voir Mode d'emploi de l'éditeur de messages).
    Nouveau sur le forum ? Consultez Les Règles du Club.

  13. #13
    Membre du Club
    Homme Profil pro
    Inscrit en
    Février 2013
    Messages
    95
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2013
    Messages : 95
    Points : 41
    Points
    41
    Par défaut
    Merci ça fonctionne !
    J'essayerai d'améliorer mon code demain (vu qu'il existe des méthodes plus adaptées) ^^

    Quel est la meilleure voie à suivre? Un executors où cette méthode de principe ^^

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Problème wait() et notifyAll()
    Par Liiinz dans le forum Agents de placement/Fenêtres
    Réponses: 1
    Dernier message: 12/09/2011, 20h28
  2. Réponses: 1
    Dernier message: 23/07/2009, 09h02
  3. Problème /wait (invite de commande dos)
    Par ludo.guy dans le forum Scripts/Batch
    Réponses: 4
    Dernier message: 13/10/2008, 10h48
  4. [Débutant] Synchronisation - wait() et notifyAll()
    Par Mattius007 dans le forum Langage
    Réponses: 7
    Dernier message: 14/01/2008, 12h27
  5. [débutant][Thread] wait et NotifyAll
    Par norkius dans le forum Concurrence et multi-thread
    Réponses: 2
    Dernier message: 14/03/2005, 15h28

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo