Comment utiliser l'attente et la notification en Java sans IllegalMonitorStateException?

129

J'ai 2 matrices et je dois les multiplier puis imprimer les résultats de chaque cellule. Dès qu'une cellule est prête, je dois l'imprimer, mais par exemple, je dois imprimer la cellule [0] [0] avant la cellule [2] [0] même si le résultat de [2] [0] est prêt en premier . J'ai donc besoin de l'imprimer sur commande. Donc, mon idée est de faire attendre le fil de l'imprimante jusqu'à ce que le multiplyThreadnotifie que la cellule correcte est prête à être imprimée, puis l' printerThreadimprimera la cellule et retournera à l'attente, etc.

J'ai donc ce fil qui fait la multiplication:

public void run() 
{
    int countNumOfActions = 0; // How many multiplications have we done
    int maxActions = randomize(); // Maximum number of actions allowed

    for (int i = 0; i < size; i++)
    {       
        result[rowNum][colNum] = result[rowNum][colNum] + row[i] * col[i];
        countNumOfActions++;
        // Reached the number of allowed actions
        if (countNumOfActions >= maxActions)
        {
            countNumOfActions = 0;
            maxActions = randomize();
            yield();
        }   
    }
    isFinished[rowNum][colNum] = true;
    notify();
}

Thread qui imprime le résultat de chaque cellule:

public void run()
{
    int j = 0; // Columns counter
    int i = 0; // Rows counter
    System.out.println("The result matrix of the multiplication is:");

    while (i < creator.getmThreads().length)
    {
        synchronized (this)
        {
            try 
            {
                this.wait();
            } 
            catch (InterruptedException e1) 
            {
            }
        }
        if (creator.getmThreads()[i][j].getIsFinished()[i][j] == true)
        {
            if (j < creator.getmThreads()[i].length)
            {
                System.out.print(creator.getResult()[i][j] + " ");
                j++;
            }
            else
            {
                System.out.println();
                j = 0;
                i++;
                System.out.print(creator.getResult()[i][j] + " ");
            }
        }
    }

Maintenant, cela me jette ces exceptions:

Exception in thread "Thread-9" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-6" Exception in thread "Thread-4" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-5" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-8" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-7" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-11" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-10" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-12" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)

la ligne 49 multiplyThreadest le "notify ()" .. Je pense que je dois utiliser le synchronisé différemment mais je ne sais pas comment.

Si quelqu'un peut aider ce code à fonctionner, je l'apprécierai vraiment.

Radiodef
la source

Réponses:

215

Pour pouvoir appeler notify (), vous devez vous synchroniser sur le même objet.

synchronized (someObject) {
    someObject.wait();
}

/* different thread / object */
synchronized (someObject) {
    someObject.notify();
}
Bombe
la source
29
L' while(!JobCompleted);option est généralement une mauvaise idée car elle lie votre processeur à 100% en vérifiant constamment la même variable (voir ici )
Matt Lyons
5
while(!JobCompleted) Thread.sleep(5); n'a pas ce problème
BeniBela
15
Il a toujours le problème d'être quelque chose de complètement différent. L'interrogation (vérifier à plusieurs reprises si une condition est remplie, c'est-à-dire ce que vous faites) est généralement moins préférée que d'être notifiée si ladite condition est modifiée (c'est-à-dire ce que j'ai décrit dans la réponse).
Bombe
3
@huseyintugrulbuyukisik vous pouvez appeler waitchaque fois que le thread actuel a un verrou sur l'objet sur lequel waitest appelé. Que vous utilisiez un synchronizedbloc ou une méthode synchronisée dépend entièrement de vous.
Bombe
1
@BeniBela Mais on peut s'attendre à ce que ce soit plus lent à coup sûr (en ce qui concerne la question initiale de Huseyin).
Thomas
64

Lors de l'utilisation des méthodes waitet notifyou notifyAllen Java, les points suivants doivent être gardés à l'esprit:

  1. Utilisez notifyAllplutôt que notifysi vous prévoyez que plusieurs threads attendent un verrou.
  2. Les méthodes waitand notifydoivent être appelées dans un contexte synchronisé . Voir le lien pour une explication plus détaillée.
  3. Appelez toujours la wait()méthode dans une boucle car si plusieurs threads attendent un verrou et que l'un d'eux a obtenu le verrou et réinitialisé la condition, les autres threads doivent vérifier la condition après leur réveil pour voir s'ils doivent attendre à nouveau ou peut commencer le traitement.
  4. Utilisez le même objet pour l'appel wait()et la notify()méthode; chaque objet a son propre verrou, donc appeler l' wait()objet A et l' notify()objet B n'aura aucun sens.
Jackob
la source
21

Avez-vous besoin d'enfiler cela du tout? Je me demande quelle est la taille de vos matrices et s'il y a un avantage à avoir un fil d'impression pendant que l'autre fait la multiplication.

Peut-être vaut-il la peine de mesurer ce temps avant de faire le travail de filetage relativement complexe?

Si vous avez besoin de l'enfiler, je créerais des threads `` n '' pour effectuer la multiplication des cellules (peut-être que `` n '' est le nombre de cœurs disponibles), puis utiliserais le mécanisme ExecutorService et Future pour distribuer plusieurs multiplications simultanément .

De cette façon, vous pouvez optimiser le travail en fonction du nombre de cœurs et vous utilisez les outils de thread Java de niveau supérieur (ce qui devrait vous faciliter la vie). Réécrivez les résultats dans une matrice de réception, puis imprimez-la simplement une fois que toutes vos tâches futures sont terminées.

Brian Agnew
la source
1
+1 @Greg Je pense que vous devriez jeter un oeil au package java.util.concurrent, comme l'a souligné Brian.
ATorras
1
+1 et consultez également ce livre qui vous apprendra également la bonne façon d'utiliser wait () et notify () jcip.net
Chii
14

Disons que vous avez une application 'boîte noire' avec une classe nommée BlackBoxClassqui a une méthode doSomething();.

De plus, vous avez nommé un observateur ou un auditeur onResponse(String resp)qui sera appelé BlackBoxClassaprès un temps inconnu.

Le flux est simple:

private String mResponse = null; 
 ...
BlackBoxClass bbc = new BlackBoxClass();
   bbc.doSomething();
...
@override
public void onResponse(String resp){        
      mResponse = resp;       
}

Disons que nous ne savons pas ce qui se passe BlackBoxClasset quand nous devrions obtenir une réponse, mais vous ne voulez pas continuer votre code jusqu'à ce que vous obteniez une réponse ou en d'autres termes un onResponseappel. Entrez ici 'Synchronize helper':

public class SyncronizeObj {
public void doWait(long l){
    synchronized(this){
        try {
            this.wait(l);
        } catch(InterruptedException e) {
        }
    }
}

public void doNotify() {
    synchronized(this) {
        this.notify();
    }
}

public void doWait() {
    synchronized(this){
        try {
            this.wait();
        } catch(InterruptedException e) {
        }
    }
}
}

Maintenant, nous pouvons mettre en œuvre ce que nous voulons:

public class Demo {

private String mResponse = null; 
 ...
SyncronizeObj sync = new SyncronizeObj();

public void impl(){

BlackBoxClass bbc = new BlackBoxClass();
   bbc.doSomething();

   if(mResponse == null){
      sync.doWait();
    }

/** at this momoent you sure that you got response from  BlackBoxClass because
  onResponse method released your 'wait'. In other cases if you don't want wait too      
  long (for example wait data from socket) you can use doWait(time) 
*/ 
...

}


@override
public void onResponse(String resp){        
      mResponse = resp;
      sync.doNotify();       
   }

}
Maxime Shoustin
la source
7

Vous ne pouvez appeler la notification que sur les objets pour lesquels vous possédez leur moniteur. Alors tu as besoin de quelque chose comme

synchronized(threadObject)
{
   threadObject.notify();
}
PaulJWilliams
la source
6

notify() doit également être synchronisé

takete.dk
la source
3

Je vais juste un exemple simple vous montrer la bonne façon d'utiliser waitet notifyen Java. Je vais donc créer deux classes nommées ThreadA & ThreadB . ThreadA appellera ThreadB.

public class ThreadA {
    public static void main(String[] args){
        ThreadB b = new ThreadB();//<----Create Instance for seconde class
        b.start();//<--------------------Launch thread

        synchronized(b){
            try{
                System.out.println("Waiting for b to complete...");
                b.wait();//<-------------WAIT until the finish thread for class B finish
            }catch(InterruptedException e){
                e.printStackTrace();
            }

            System.out.println("Total is: " + b.total);
        }
    }
} 

et pour la classe ThreadB:

class ThreadB extends Thread{
    int total;
    @Override
    public void run(){
        synchronized(this){
            for(int i=0; i<100 ; i++){
                total += i;
            }
            notify();//<----------------Notify the class wich wait until my    finish 
//and tell that I'm finish
            }
        }
    }
BERGUIGA Mohamed Amine
la source
3

Utilisation simple si vous voulez Comment exécuter des threads alternativement: -

public class MyThread {
    public static void main(String[] args) {
        final Object lock = new Object();
        new Thread(() -> {
            try {
                synchronized (lock) {
                    for (int i = 0; i <= 5; i++) {
                        System.out.println(Thread.currentThread().getName() + ":" + "A");
                        lock.notify();
                        lock.wait();
                    }
                }
            } catch (Exception e) {}
        }, "T1").start();

        new Thread(() -> {
            try {
                synchronized (lock) {
                    for (int i = 0; i <= 5; i++) {
                        System.out.println(Thread.currentThread().getName() + ":" + "B");
                        lock.notify();
                        lock.wait();
                    }
                }
            } catch (Exception e) {}
        }, "T2").start();
    }
}

réponse: -

T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
Opster Elasticsearch Pro-Vijay
la source
Comment cela fonctionne-t-il lorsque j'ai 4 opérations à effectuer de manière synchrone?
saksham agarwal
2

on peut appeler notify pour reprendre l'exécution des objets en attente comme

public synchronized void guardedJoy() {
    // This guard only loops once for each special event, which may not
    // be the event we're waiting for.
    while(!joy) {
        try {
            wait();
        } catch (InterruptedException e) {}
    }
    System.out.println("Joy and efficiency have been achieved!");
}

reprendre cela en invoquant notifier sur un autre objet de la même classe

public synchronized notifyJoy() {
    joy = true;
    notifyAll();
}
Greesh Kumar
la source
0

Pour ce problème particulier, pourquoi ne pas stocker vos différents résultats dans des variables et ensuite, lorsque le dernier de votre fil est traité, vous pouvez imprimer dans le format de votre choix. Ceci est particulièrement utile si vous comptez utiliser votre historique de travail dans d'autres projets.

kbluue
la source
0

Cela ressemble à une situation pour le modèle producteur-consommateur. Si vous utilisez java 5 ou plus, vous pouvez envisager d'utiliser la file d'attente de blocage (java.util.concurrent.BlockingQueue) et laisser le travail de coordination des threads à l'implémentation du framework / api sous-jacent. Voir l'exemple de java 5: http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/BlockingQueue.html ou java 7 (même exemple): http: // docs. oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html

user3044236
la source
0

Vous avez correctement protégé votre bloc de code lorsque vous appelez wait()method en utilisant synchronized(this).

Mais vous n'avez pas pris la même précaution lorsque vous appelez notify()method sans utiliser de bloc gardé: synchronized(this)ousynchronized(someObject)

Si vous faites référence à la page de documentation Oracle sur l' objet de classe, qui contient wait(), notify(), les notifyAll()méthodes, vous pouvez voir ci - dessous précaution dans ces trois méthodes

Cette méthode ne doit être appelée que par un thread propriétaire du moniteur de cet objet

Beaucoup de choses ont changé au cours des 7 dernières années et examinons d'autres alternatives aux synchronizedquestions SE ci-dessous:

Pourquoi utiliser un ReentrantLock si on peut utiliser synchronized (this)?

Synchronisation vs verrouillage

Évitez de synchroniser (ceci) en Java?

Ravindra babu
la source