Thread: IllegalMonitorStateException

di il
28 risposte

Thread: IllegalMonitorStateException

Mentre rivedevo alcuni esercizi sui thread mi sono imbattuto in un problema e cioè nella IllegalMonitorStateException.
Vi posto un po' di codice perchè vorrei chiarirmi questo dubbio.
Allora, io ho un'applicazione che permette di effettuare degli acquisti e ogni volta che si decide di effettuare un acquisto (dei souvenir) mi crea un thread in questo modo:

private void nuovoAcquisto() {
    AttivitaPrincipale attivitaPrincipale = new AttivitaPrincipale();
    Thread t = new Thread(attivitaPrincipale);
    t.start();
  }
All'interno del suo metodo run() ci sono delle if per poter selezionare il tipo di souvenir acquistato, ad esempio questo è l'if per i magneti:

public synchronized void run() {
    if (eseguita)
      return;
    eseguita = true;
    
    // Chiedi tipo souvenir
    tipoAcquisti = AttivitaIO.chiediTipoSouvenir();

    CreaNuovoScontrino creaScontrino = new CreaNuovoScontrino();
    Executor.perform(creaScontrino);
    scontrinoCorrente = creaScontrino.getRisultato();

    Thread sottoRamoMagneti = null;
    Thread sottoRamoBerretti = null;

    [b]if (tipoAcquisti.isMagneti()) {
      sottoRamoMagneti = new Thread(new AttivitaSottoramoMagneti(scontrinoCorrente));
      sottoRamoMagneti.start();
      try {
          Thread.currentThread().wait();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }[/b]
   ...
}
l'eccezione viene sollevata nel momento in cui invoco il metodo wait() sul thread corrente perchè in realtà non ho il monitor di quell'oggetto, ma come è possibile questa cosa? cioè come faccio a non avere il monitor di un thread in esecuzione?

28 Risposte

  • Re: Thread: IllegalMonitorStateException

    newutente ha scritto:


    ma come è possibile questa cosa? cioè come faccio a non avere il monitor di un thread in esecuzione?
    Il run() è della classe AttivitaPrincipale? (che deve certamente essere un Runnable, per quanto se ne deduce).
    Se sì, allora il synchronized sul run acquisisce il lock dell'oggetto AttivitaPrincipale ... che chiaramente NON è il java.lang.Thread.

    Poi comunque, è vero che un java.lang.Thread alla fin fine è un oggetto come un altro e ha i suoi bei wait(), notify() ecc... Ma è comunque un qualcosa di un po' più particolare e io personalmente non andrei mai ad acquisire un lock su un java.lang.Thread!

    E ad ogni modo, il wait non andrebbe nemmeno usato così. Va fatto in un ciclo che testa una condizione che è quella che deve "reggere" affinché il thread rimanga in attesa. (pensa ad una coda "bloccante", il metodo di estrazione si blocca finché la coda è vuota).
  • Re: Thread: IllegalMonitorStateException

    andbin ha scritto:


    Il run() è della classe AttivitaPrincipale? (che deve certamente essere un Runnable, per quanto se ne deduce).
    Se sì, allora il synchronized sul run acquisisce il lock dell'oggetto AttivitaPrincipale ... che chiaramente NON è il java.lang.Thread.

    Poi comunque, è vero che un java.lang.Thread alla fin fine è un oggetto come un altro e ha i suoi bei wait(), notify() ecc... Ma è comunque un qualcosa di un po' più particolare e io personalmente non andrei mai ad acquisire un lock su un java.lang.Thread!
    si, il run() è della classe AttivitaPrincipale che è un Runnable.
    per quanto riguarda il lock io ho provato a stampare il nome del currentThread e mi riporta che è proprio il thread di AttivitaPrincipale. Stando a quello che dici non doveva essere così però e doveva essere il thread del main. Come mai questa cosa?

    andbin ha scritto:


    E ad ogni modo, il wait non andrebbe nemmeno usato così. Va fatto in un ciclo che testa una condizione che è quella che deve "reggere" affinché il thread rimanga in attesa. (pensa ad una coda "bloccante", il metodo di estrazione si blocca finché la coda è vuota).
    io sinceramente avevo pensato di utilizzare notify() per sbloccare il thread bloccato tramite wait() facendo il metodo run() di AttivitaSottoramoMagneti (che è sempre un Runnable) così:
    
    public synchronized void run() {
        if (eseguita)
          return;
        eseguita = true;
    
        while (ancoraMagneti) {
            LeggiMagneti object = new LeggiMagneti();
        	Set<String> result = object.getRisultato();
            RecordAcquisto magnete = AttivitaIO.mostraSouvenirPerSelezione(result);
            AggiornaScontrino aggiorna = new AggiornaScontrino(magnete, scontrinoCorrente);
            if(AttivitaIO.altroMagnete()) {
          	  ancoraMagneti = true;
            }
        }
        notify();
      }
    
    in questo modo a seconda del flag ancoraMagneti avviene lo sblocco oppure no. Non so però se è corretta come cosa.
  • Re: Thread: IllegalMonitorStateException

    newutente ha scritto:


    ho provato a stampare il nome del currentThread e mi riporta che è proprio il thread di AttivitaPrincipale. Stando a quello che dici non doveva essere così però e doveva essere il thread del main. Come mai questa cosa?
    Hai fatto new AttivitaPrincipale() quindi hai istanziato un AttivitaPrincipale che è solo un Runnable.
    Poi hai fatto new Thread(attivitaPrincipale). E il Thread incapsula il tuo AttivitaPrincipale ("vedendolo" solo come Runnable).
    Ma sono due oggetti distinti, diversi, lo vedi o no?

    E siccome il synchronized sul run è nella tua classe AttivitaPrincipale, il lock acquisito è sull'oggetto AttivitaPrincipale. Non certo sul java.lang.Thread.
  • Re: Thread: IllegalMonitorStateException

    Si si, ora mi è chiaro.
  • Re: Thread: IllegalMonitorStateException

    Ho modificato un po' il metodo run() ma ora ho difficoltà con la gestione di wait() e notify().
    L'aspetto teorico dei thread mi è chiaro ma non riesco a capire perchè il metodo notify() nel mio caso non riesce a sbloccare il thread AttivitaPrincipale e continuare l'esecuzione del suo metodo run().

    Questa è la classe AttivitaPrincipale:
    
    public class AttivitaPrincipale implements Runnable {
      
      private boolean eseguita = false;
      private Scontrino scontrinoCorrente = null;
      private RecordTipoAcquisti tipoAcquisti = null;
      private RecordScontrino recordScontrino = null;
    
      public synchronized void run() {
        if (eseguita)
          return;
        eseguita = true;
        
        // Chiedi tipo souvenir
        tipoAcquisti = AttivitaIO.chiediTipoSouvenir();
    
        CreaNuovoScontrino creaScontrino = new CreaNuovoScontrino();
        Executor.perform(creaScontrino);
        scontrinoCorrente = creaScontrino.getRisultato();
    
        Thread sottoRamoMagneti = null;
        Thread sottoRamoBerretti = null;
    
        if (tipoAcquisti.isMagneti()) {
          // si' magneti
          sottoRamoMagneti = new Thread(new AttivitaSottoramoMagneti(scontrinoCorrente));
          sottoRamoMagneti.start();
          try {
        	  while(eseguita) {
        	  			this.wait();
        	  			eseguita = false;
        	  }
          } catch (InterruptedException e) {
    		e.printStackTrace();
          }
        }
    
        ...
        
        }
    }
    
    in questa classe ho usato il flag "eseguita" per gestire tramite un ciclo while il wait (come mi hai suggerito prima). Effettivamente funziona tutto perfettamente qui e l'esecuzione al thread "AttivitaSottoramoMagneti".
    Questa è la classe AttivitaSottoramoMagneti:
    
    public class AttivitaSottoramoMagneti implements Runnable {
      
      private boolean eseguita = false;
      private Scontrino scontrinoCorrente = null;
      private boolean ancoraMagneti = true;
      private Set<String> magnetiLetti;
      private RecordAcquisto magneteSelezionato;
    
      public AttivitaSottoramoMagneti(Scontrino scontrinoCorrente) {
        this.scontrinoCorrente = scontrinoCorrente;
      }
    
      public synchronized void run() {
        if (eseguita)
          return;
        eseguita = true;
    
        while (ancoraMagneti) {
            LeggiMagneti object = new LeggiMagneti();
        	Executor.perform(object);
        	Set<String> result = object.getRisultato();
            RecordAcquisto magnete = AttivitaIO.mostraSouvenirPerSelezione(result);
            AggiornaScontrino aggiorna = new AggiornaScontrino(magnete, scontrinoCorrente);
            if(AttivitaIO.altroMagnete() == false) {
          	  	ancoraMagneti = false;
          	  	notifyAll();
          	}
        }
      }
    }
    
    Ho utilizzato notifyAll() per essere sicuro di sbloccare il thread bloccato (che sarebbe AttivitaPrincipale). L'esecuzione procede senza problemi fino al settaggi del flag "ancoraMagneti" a false poi però non avviene lo sblocco del thread "AttivitaPrincipale".
    Dove sbaglio?
    Ho cercato di capire attraverso gli esempi trovati in rete ma l'utilizzo di wait() e notify() mi sembra fatto esattamente come lo faccio io solo che negli esempi funziona mentre a me no.
  • Re: Thread: IllegalMonitorStateException

    newutente ha scritto:


    Ho utilizzato notifyAll() per essere sicuro di sbloccare il thread bloccato (che sarebbe AttivitaPrincipale). L'esecuzione procede senza problemi fino al settaggi del flag "ancoraMagneti" a false poi però non avviene lo sblocco del thread "AttivitaPrincipale".
    Dove sbaglio?
    Ma i lock sono diversi!! Un lock è acquisito sull'oggetto AttivitaPrincipale e sulla sua condition-queue fai il wait().
    Poi un altro lock è acquisito sull'oggetto AttivitaSottoramoMagneti e sulla sua condition-queue fai il notifyAll().

    Sono due lock differenti, NON ti può funzionare.
  • Re: Thread: IllegalMonitorStateException

    Ma racchiudendo notifyAll() all'interno di un blocco fatto così:
    
    synchronized(AttivitaPrincipale.class) {
          	  		notifyAll();
    }
    
    non dovrebbe essere possibile acquisire il lock di AttivitaPrincipale e sbloccare tutti i thread AttivitaPrincipale?
  • Re: Thread: IllegalMonitorStateException

    newutente ha scritto:


    ma racchiudendo notifyAll() all'interno di un blocco fatto così:
    
    synchronized(AttivitaPrincipale.class) {
          	  		notifyAll();
    }
    
    non dovrebbe essere possibile acquisire il lock di AttivitaPrincipale e sbloccare tutti i thread AttivitaPrincipale?
    No. Quando metti synchronized su un metodo statico, il lock è acquisito sull'oggetto java.lang.Class relativo alla classe. Mentre su un metodo di istanza il lock è acquisito sul this.

    synchronized(AttivitaPrincipale.class) è semplicemente l'uso esplicito del lock sul Class.

    Ma è comunque un ALTRO lock rispetto al

    public synchronized void run()
    di AttivitaPrincipale

    Il lock, te lo ripeto, deve essere lo stesso!
  • Re: Thread: IllegalMonitorStateException

    Quindi se ho capito bene il metodo run() di AttivitaPrincipale essendo synchronized permette di ottenere il lock dell'oggetto corrente (il this insomma) e attraverso quell'oggetto viene invocato il metodo wait() che ferma il thread corrente (quello che ha invocato il metodo run() di AttivitaPrincipale).
    A questo punto parte il thread che invoca il metodo run() di AttivitaSottoramoMagneti ed essendo anche in questo caso sincronizzato sarà acquisito il lock dell'oggetto corrente (quindi di AttivitaSottoramoMagneti e NON di AttivitaPrincipale).
    Il problema si pone perchè il notify() invocato successivamente viene invocato sul lock di AttivitaSottoramoMagneti e NON sul lock di AttivitaSottoramoMagneti, di conseguenza l'esecuzione non riesce a trovare il thread in AttivitaPrincipale da sbloccare.
    E' corretta come analisi?
  • Re: Thread: IllegalMonitorStateException

    newutente ha scritto:


    E' corretta come analisi?
    Sì, è corretta. Sono due lock differenti, quindi due condition-queue differenti. La condition-queue sostanzialmente è un "wait-set" un insieme di thread in attesa.
    Se il thread A acquisisce il lock sull'oggetto x e poi fa un x.wait(), il thread A va in sospensione e viene messo all'interno della condition-queue relativa a x.
    Se un thread B acquisisce il lock sull'oggetto y (altro oggetto) e poi fa un y.notifyAll(), esso notificherebbe tutti i thread in attesa ma sulla condition-queue di y !! Non di x.

    P.S. Riguardando meglio il tuo codice, vedo che in AttivitaSottoramoMagneti fai:

    ancoraMagneti = false;
    notifyAll();

    Che in sostanza il false fa poi terminare il thread. Ma scusa, allora, se volevi attendere la terminazione di un thread, perché non ti basta il join() sul Thread?? Mi sfugge qualcosa?
  • Re: Thread: IllegalMonitorStateException

    andbin ha scritto:


    P.S. Riguardando meglio il tuo codice, vedo che in AttivitaSottoramoMagneti fai:

    ancoraMagneti = false;
    notifyAll();

    Che in sostanza il false fa poi terminare il thread. Ma scusa, allora, se volevi attendere la terminazione di un thread, perché non ti basta il join() sul Thread?? Mi sfugge qualcosa?
    il senso di quel flag è proprio quello, cioè mi esce una finestra per la scelta e devo decidere se continuare l'acquisto oppure no, nel caso in cui scelgo "no" viene settato il flag a false e di conseguenza termina il thread, se scelgo "si" rimane settato a true e il thread non termina visto che entra in un ciclo while infinito.
    Io la trovo sensata come cosa. dici di no?


    p.s. effettivamente l'utilizzo del join() è la scelta migliore anche se sinceramente non ho ancora capito come fare per acquisire il lock dell'oggetto AttivitaPrincipale trovandomi nella classe AttivitaSottoramoMagneti per l'utilizzo di wait() e notify().
  • Re: Thread: IllegalMonitorStateException

    newutente ha scritto:


    il senso di quel flag è proprio quello, cioè mi esce una finestra per la scelta e devo decidere se continuare l'acquisto oppure no, nel caso in cui scelgo "no" viene settato il flag a false e di conseguenza termina il thread, se scelgo "si" rimane settato a true e il thread non termina visto che entra in un ciclo while infinito.
    Io la trovo sensata come cosa. dici di no?
    Non posso ovviamente avere una idea chiara della tua applicazione e di cosa vuoi fare ma ... di quale interfaccia grafica parli? La interazione tra thread qualunque e la interfaccia grafica (specialmente Swing) è notoriamente "delicata" e da fare in modo attento e accurato.

    newutente ha scritto:


    p.s. effettivamente l'utilizzo del join() è la scelta migliore anche se sinceramente non ho ancora capito come fare per acquisire il lock dell'oggetto AttivitaPrincipale
    Per usare join() non serve alcun lock. threadX.join() manda in sospensione il thread corrente (quello che sta invocando il join) fino a quando il thread relativo al java.lang.Thread threadX termina la sua esecuzione.
  • Re: Thread: IllegalMonitorStateException

    andbin ha scritto:


    Non posso ovviamente avere una idea chiara della tua applicazione e di cosa vuoi fare ma ... di quale interfaccia grafica parli? La interazione tra thread qualunque e la interfaccia grafica (specialmente Swing) è notoriamente "delicata" e da fare in modo attento e accurato.
    l'interfaccia grafica è fatta usando la libreria Swing e permette la selezione del souvenir da acquistare e a seconda delle scelte permette di interrompere un thread o no (es. se acquisto un magnete parte il thread di "AttivitaSottoramoMagneti" e dopo l'acquisto posso decidere se continuare oppure se interrompere il thread), questo è grosso modo il funzionamento.

    andbin ha scritto:


    Per usare join() non serve alcun lock. threadX.join() manda in sospensione il thread corrente (quello che sta invocando il join) fino a quando il thread relativo al java.lang.Thread threadX termina la sua esecuzione.
    per assurdo però un codice di questo tipo:
    
    if (tipoAcquisti.isMagneti()) {
          // si' magneti
          sottoRamoMagneti = new Thread(new AttivitaSottoramoMagneti(scontrinoCorrente));
          sottoRamoMagneti.start();
          try {
        	 sottoRamoMagneti.join();
          } catch (InterruptedException e) {
    		e.printStackTrace();
          }
    }
    
    inserito all'interno del metodo run() di AttivitaPrincipale fa fermare il thread di AttivitaPrincipale fino a che quello di AttivitaSottoramoMagneti non è completato. Me ne accorgo di questa cosa perchè quando termina il run() di AttivitaSottoramoMagneti continua il run() di AttivitaPrincipale.
    la cosa strana è che è un comportamento opposto a quello che dovrebbe avere come giustamente mi fai notare tu.
  • Re: Thread: IllegalMonitorStateException

    newutente ha scritto:


    l'interfaccia grafica è fatta usando la libreria Swing e permette la selezione del souvenir da acquistare e a seconda delle scelte permette di interrompere un thread o no (es. se acquisto un magnete parte il thread di "AttivitaSottoramoMagneti" e dopo l'acquisto posso decidere se continuare oppure se interrompere il thread), questo è grosso modo il funzionamento.
    E cosa fa, giusto per dire, questa parte??
        tipoAcquisti = AttivitaIO.chiediTipoSouvenir();
    
        CreaNuovoScontrino creaScontrino = new CreaNuovoScontrino();
        Executor.perform(creaScontrino);
        scontrinoCorrente = creaScontrino.getRisultato();

    newutente ha scritto:


    la cosa strana è che è un comportamento opposto a quello che dovrebbe avere come giustamente mi fai notare tu.
    Ma è proprio quello che ho detto io in generale. Al join il thread di AttivitaPrincipale va in sospensione e si esce dal join solo quando il thread di AttivitaSottoramoMagneti termina.
Devi accedere o registrarti per scrivere nel forum
28 risposte