web-dev-qa-db-fra.com

Comment utiliser wait and notify dans Java sans IllegalMonitorStateException?

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 par commande. Donc, mon idée est de faire en sorte que le fil d'impression attende que le multiplyThread lui notifie que la bonne cellule est prête à être imprimée, puis le printerThread imprimera la cellule et reviendra en 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();
}

Fil qui affiche 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, il 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 dans multiplyThread est "notify ()" .. Je pense que je dois utiliser le mode synchronisé différemment, mais je ne sais pas comment.

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

126
Greg

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

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

/* different thread / object */
synchronized (someObject) {
    someObject.notify();
}
209
Bombe

Lorsque vous utilisez les méthodes wait et notify ou notifyAll dans Java, vous devez vous rappeler les points suivants:

  1. Utilisez notifyAll au lieu de notify si vous vous attendez à ce que plusieurs threads attendent un verrou.
  2. Les méthodes wait et notify doivent être appelées dans un contexte synchronisé . Voir le lien pour une explication plus détaillée.
  3. Appelez toujours la méthode wait() dans une boucle, car si plusieurs threads attendent un verrou et l’un d’eux a le verrouillé et réinitialise la condition, les autres threads doivent alors vérifier la condition après leur réveil pour voir s’ils se réveillent. besoin d'attendre encore ou peut commencer le traitement.
  4. Utilisez le même objet pour appeler la méthode wait() et notify(); chaque objet a son propre verrou donc appeler wait() sur l'objet A et notify() sur l'objet B n'aura aucun sens.
63
Jackob

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

Peut-être vaudrait-il la peine de mesurer ce temps avant d’effectuer le travail de filetage relativement complexe?

Si vous avez besoin de le filer, je créerais des "n" fils pour effectuer la multiplication des cellules (peut-être que "n" est le nombre de cœurs disponibles), puis utiliser ExecutorService et Future mécanisme d'envoi simultané de multiples multiplications.

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

20
Brian Agnew

Supposons que vous ayez une application 'boîte noire' avec une classe nommée BlackBoxClass utilisant la méthode doSomething();.

De plus, vous avez un observateur ou un auditeur nommé onResponse(String resp) qui sera appelé par BlackBoxClass aprè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 avec BlackBoxClass et quand nous devrions obtenir une réponse, mais vous ne voulez pas continuer votre code tant que vous n'avez pas reçu de réponse ou dans un autre mot, appelez onResponse. Ici entre '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();       
   }

}
13
Maxim Shoustin

Vous pouvez uniquement appeler notify sur les objets pour lesquels vous possédez leur moniteur. Donc vous avez besoin de quelque chose comme

synchronized(threadObject)
{
   threadObject.notify();
}
7
PaulJWilliams

notify() doit également être synchronisé

6
takete.dk

Je vais vous donner un exemple simple et vous montrer comment utiliser wait et notify en 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
            }
        }
    }
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
2
Vijay Gupta

nous pouvons appeler notify pour reprendre l'exécution d'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!");
}

résumez ceci en appelant notify sur un autre objet de la même classe

public synchronized notifyJoy() {
    joy = true;
    notifyAll();
}
2
Girish Kumar

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

0
kbluue

Vous avez correctement protégé votre bloc de code lorsque vous appelez la méthode wait() à l'aide de synchronized(this).

Mais vous n’avez pas pris la même précaution lorsque vous appelez la méthode notify() sans utiliser de bloc protégé: synchronized(this) ou synchronized(someObject)

Si vous vous référez à la page de documentation Oracle sur la classe Object , qui contient les méthodes wait(), notify(), notifyAll(), vous pouvez voir ci-dessous les précautions d'utilisation de 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 été changées au cours des 7 dernières années et examinons d'autres alternatives à synchronized dans les questions SE suivantes:

Pourquoi utiliser un ReentrantLock si on peut utiliser synchronisé (ceci)?

Synchronization vs Lock

Evitez synchronisé (ceci) en Java?

0
Ravindra babu

Cela ressemble à une situation pour le modèle producteur-consommateur. Si vous utilisez Java 5 ou version ultérieure, vous pouvez envisager d’utiliser une file d’attente (Java.util.concurrent.BlockingQueue) et laisser le travail de coordination des threads à l’implémentation 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

0
user3044236