web-dev-qa-db-fra.com

java.lang.IllegalMonitorStateException: (m = null) Échec d'obtention du moniteur pour

Pourquoi cela peut-il arriver? Le problème est que l’objet moniteur n’est pas nul, mais nous avons toujours cette exception assez souvent:

Java.lang.IllegalMonitorStateException: (m=null) Failed to get monitor for (tIdx=60)
        at Java.lang.Object.wait(Object.Java:474)
        at ...

Le code qui provoque cela est une solution de pool simple:

    public Object takeObject() {
        Object obj = internalTakeObject();
        while (obj == null) {
            try {
                available.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            obj = internalTakeObject();
        }
        return obj;
    }

    private Object internalTakeObject() {
        Object obj = null;
        synchronized (available) {
            if (available.size() > 0) {
                obj = available.keySet().iterator().next();
                available.remove(obj);
                synchronized (taken) {
                    taken.put(obj, Boolean.valueOf(true));
                }
            }
        }
        return obj;
    }

    public void returnObject(Object obj) {
        synchronized (taken) {
            taken.remove(obj);
        }
        synchronized (available) {
            if (available.size() < size) {
                available.put(obj, Boolean.valueOf(true));
                available.notify();
            }
        }
    }

Est-ce que je manque quelque chose?

EDIT: l'exception se produit dans la ligne available.wait();.

34
Andrey Adamovich

Voir le javadoc pour Object.wait.

en particulier "Le thread actuel doit posséder le moniteur de cet objet." et "[lève] IllegalMonitorStateException - si le thread actuel n'est pas le propriétaire du moniteur de l'objet." C'est-à-dire que vous devez synchroniser sur l'objet que vous allez appeler.

alors votre code devrait être:

synchronized (available) {
    available.wait();
}
65
tgdavies

available.wait(); doit être dans une section synchronisée (disponible)

7
Maurice Perry

Vous obtenez le "IllegalMonitorStateException" de

available.wait()

car le thread actuel qui appelle la méthode wait () n'est pas le propriétaire du moniteur de l'Object qui est référencé par la référence d'objet "disponible".

Pour qu'un thread devienne propriétaire du moniteur d'un objet, il y a 3 façons.

  1. En exécutant une méthode d'instance synchronisée de cet objet.
  2. En exécutant le corps d'un bloc synchronisé qui se synchronise sur l'objet.
  3. Pour les objets de type Classe en exécutant une méthode statique synchronisée de cette classe.

Exemple de code simple pour chaque scénario. Les trois extraits de code sont des classes distinctes pour chaque type. Copiez simplement le code et exécutez-le. J'ai ajouté de nombreux commentaires dans le code pour expliquer ce qui se passait dans chaque cas. Si c'est trop de commentaires pour vous. supprimez-les simplement pour rendre le code plus concis.

Lisez également le code de la méthode main () en premier pour vous faire une idée sur threadOne et threadTwo en premier.

  1. En exécutant une méthode d'instance synchronisée de cet objet.

    import static Java.lang.System.out;
    
    public class SynchronizedInstanceMethodClass {
    
        synchronized void synchronizedInstanceMethod() { // threadOne acquire the monitor for "this" and continue.
    
                try {
    
                    out.println("EVENT #1 threadOne is about to strat waiting on the "
                            +"monitor it already has - [\"this\"]....");
    
                    this.wait(); // The threadOne already have the monitor for "this", 
                                //  just release the monitor and go and wait threadOne.
    
                    out.println("EVENT #3 Notify received and continue execution...");
    
                } catch (InterruptedException interruptedException) {
                    interruptedException.printStackTrace();
                }
        }
    
    
        synchronized void notifierForAllThreads() { // threadTwo acquire the monitor for "this", 
                                                   // which was released by threadOne when it went to waiting and contine.
    
                out.println("EVENT #2 threadTwo is about to notify all threads(including threadOne) "
                        +"   waiting on the monitor of -[\"this\"]....");
    
                this.notifyAll(); // threadTwo who owns the monitor on "this" notifies all 
                                 // threads waiting on "this" and releases the monitor
        }
    
        public static void main(String [] args) {
    
            SynchronizedInstanceMethodClass mc  = new SynchronizedInstanceMethodClass();
            Thread threadOne = new Thread(() -> {mc.synchronizedInstanceMethod();});
            Thread threadTwo = new Thread(() -> {mc.notifierForAllThreads();});
    
            threadOne.start(); // Start the waiting of Thread one
            threadTwo.start(); // Notify the waiting threadOne
        }
    
    }
    
  2. En exécutant le corps d'un bloc synchronisé qui se synchronise sur l'objet.

    import static Java.lang.System.out;
    
    public class SynchronizedBlockClass {
    
        void synchronizedBlockInstanceMethod() {
    
            synchronized (this) { // threadOne acquire the monitor for "this" and continue.
    
                try {
    
                    out.println("EVENT #1 threadOne is about to strat waiting on the "
                                +"monitor it already has - [\"this\"]....");
    
                    this.wait(); // The threadOne already have the monitor for "this", 
                                //  just release the monitor and go and wait threadOne.
    
                    out.println("EVENT #3 Notify received and continue execution...");
                } catch (InterruptedException interruptedException) {
                    interruptedException.printStackTrace();
                }
            }
        }
    
    
        void synchronizedBlockNotifierForAllThreads() {
    
            synchronized (this) { // threadTwo acquire the monitor for "this", 
                                 // which was released by threadOne when it went to waiting and continue.
    
                    out.println("EVENT #2 threadTwo is about to notify all threads(including threadOne) "
                            +"   waiting on the monitor of -[\"this\"]....");
    
                    this.notifyAll(); // threadTwo who owns the monitor on "this" notifies all 
                                     // threads waiting on "this" and releases the monitor
                }
        }
    
        public static void main(String [] args) {
            SynchronizedBlockClass mc  = new SynchronizedBlockClass();
            Thread threadOne = new Thread(() -> {mc.synchronizedBlockInstanceMethod();});
            Thread threadTwo = new Thread(() -> {mc.synchronizedBlockNotifierForAllThreads();});
    
            threadOne.start(); // Start the waiting of Thread one
            threadTwo.start(); // Notify the waiting threadOne
        }
    
    }
    
  3. Pour les objets de type Classe en exécutant une méthode statique synchronisée de cette classe.

    import static Java.lang.System.out;
    
    public class StaticClassReferenceClass {
    
        void synchronizedBlockInstanceMethod() {
    
            synchronized (StaticClassReferenceClass.class) { // threadOne acquire the monitor for class literal and continue.
    
                try {
    
                    out.println("EVENT #1 threadOne is about to strat waiting on the "
                                +"monitor it already has - [StaticClassReferenceClass.class]....");
    
                    StaticClassReferenceClass.class.wait(); // The threadOne already have the monitor for the class literal, 
                                //  So it just release the monitor and go and wait.
    
                    out.println("EVENT #3 Notify received and continue execution...");
                } catch (InterruptedException interruptedException) {
                    interruptedException.printStackTrace();
                }
            }
        }
    
    
        void synchronizedBlockNotifierForAllThreads() {
    
            synchronized (StaticClassReferenceClass.class) { // threadTwo acquire the monitor for the class literal, 
                                 // which was released by threadOne when it went to waiting.
    
                    out.println("EVENT #2 threadTwo is about to notify all threads(including threadOne) "
                            +"   waiting on the monitor of -[StaticClassReferenceClass.class]....");
    
                    StaticClassReferenceClass.class.notifyAll(); // threadTwo who owns the monitor on the class literal notifies all 
                                     // threads waiting on it and releases the monitor
                }
        }
    
        public static void main(String [] args) {
            StaticClassReferenceClass mc  = new StaticClassReferenceClass();
            Thread threadOne = new Thread(() -> {mc.synchronizedBlockInstanceMethod();});
            Thread threadTwo = new Thread(() -> {mc.synchronizedBlockNotifierForAllThreads();});
    
            threadOne.start(); // Start the waiting of Thread one
            threadTwo.start(); // Notify the waiting threadOne
        }
    
    }
    
0

la méthode takeObject () doit être synchronisée ou, nous devons écrire un bloc synchronisé dans cette méthode. J'espère que vous devriez obtenir une exception de compilation pour cela.

0
prasad