Pagina 1 di 2 1 2 ultimoultimo
Visualizzazione dei risultati da 1 a 10 su 12
  1. #1

    dubbio su funzionamento wait/notifyAll

    Ciao a tutti!
    Avrei bisogno di alcune chiarificazioni riguardo il funzionamento della sincronizzazione usanda i metodi wait e notifyAll.
    Di base, il metodo wait, se chiamato, rilascia il lock sull'oggetto e il metodo notifyAll sveglia tutti i thread dal wait set in attesa su quell'oggetto e solo uno verrà eseguito.
    Ma leggendo il codice, non capisco bene il giro del fumo che fa il codice dopo la chiamata al metodo wait(). Dopo la chiamata a wait(), come il codice raggiunge il metodo notifyAll() ???

    Per esempio su questo semplice codice: supponendo che lancio n thread dal main e li gestisco con un ciclo FOR.
    codice:
    public synchronized void waitMethod()  {
    		try{
    		wait(1000);
    		
    		System.out.println("DONE! " );
    		}
    		catch (InterruptedException e) {
    			// TODO: handle exception
    		}
    		notifyAll();
    	}

  2. #2
    Utente di HTML.it L'avatar di andbin
    Registrato dal
    Jan 2006
    residenza
    Italy
    Messaggi
    18,284

    Re: dubbio su funzionamento wait/notifyAll

    Originariamente inviato da maninblack
    Di base, il metodo wait, se chiamato, rilascia il lock sull'oggetto
    Sì.

    Originariamente inviato da maninblack
    il metodo notifyAll sveglia tutti i thread dal wait set in attesa su quell'oggetto e solo uno verrà eseguito.
    Sì, solo uno perché i thread svegliati si metteranno a "competere" normalmente tra di loro per acquisire il lock. Ma quale di questi riuscirà per primo ad acquisirlo .... appunto non si sa.

    Originariamente inviato da maninblack
    Ma leggendo il codice, non capisco bene il giro del fumo che fa il codice dopo la chiamata al metodo wait(). Dopo la chiamata a wait(), come il codice raggiunge il metodo notifyAll() ???
    Il codice postato non mi pare abbia granché senso. In genere si hanno almeno 2 metodi: uno che fa "qualcosa" tale per cui dopo ha senso notificare altri thread e un altro che aspetta "qualcosa".
    Pensa al classico esempio di coda "bloccante". Un metodo es. insert() inserisce un elemento (e se la coda è a dimensione fissa potrebbe fare: finché piena metti in wait). Un altro metodo es. extract() estrae un elemento ma facendo prima: finché vuota metti in wait.

    Come vedi c'è (e ci dovrebbe) sempre una condizione da testare in ciclo: finché "qualcosa" metti in wait.
    Andrea, andbin.devSenior Java developerSCJP 5 (91%) • SCWCD 5 (94%)
    java.util.function Interfaces Cheat SheetJava Versions Cheat Sheet

  3. #3
    Si scusa il codice postato era solo a titolo di esempio anche se stupido.
    Ovviamente un buon esempio sarebbe il classico produttore-consumatore. Ma la cosa di fondo che non mi è chiara e che quando chiamo la funzione wait() il codice continua la sua esecuzione dalla linea successiva o viene chiamata la notifyAll o notify?? lo so che è una domanda stupida magari se hai un pezzettino di codice da postare mi faresti un gran favore, giusto per capire al 100%.
    Grazie mille

  4. #4
    Utente di HTML.it L'avatar di andbin
    Registrato dal
    Jan 2006
    residenza
    Italy
    Messaggi
    18,284
    Originariamente inviato da maninblack
    magari se hai un pezzettino di codice da postare mi faresti un gran favore, giusto per capire al 100%.
    Ecco un esempio di classe per "depositare" un oggetto e gestire l'inserimento e estrazione in modo "bloccante".

    codice:
    public class DepositoOggetto {
        private boolean disponibile;
        private Object obj;
    
        public synchronized void inserisci(Object obj) throws InterruptedException {
            // Finché qualcun'altro non ha ancora preso l'oggetto, attendi
            while (disponibile) {
                wait();
            }
    
            this.obj = obj;
            disponibile = true;
            notifyAll();
        }
    
        public synchronized Object estrai() throws InterruptedException {
            // Finché qualcun'altro non ha ancora inserito un oggetto, attendi
            while (!disponibile) {
                wait();
            }
    
            disponibile = false;
            Object retObj = obj;
            obj = null;
            notifyAll();
    
            return retObj;
        }
    }
    Come si può vedere è concettualmente "simmetrico". inserisci() attende se l'oggetto non è ancora stato preso mentre estrai() attende se non c'è un oggetto disponibile.
    Andrea, andbin.devSenior Java developerSCJP 5 (91%) • SCWCD 5 (94%)
    java.util.function Interfaces Cheat SheetJava Versions Cheat Sheet

  5. #5
    Grazie per la risposta celere.
    L'esempio è chiaro, ma quindi dopo la chiamata di wait() del primo metodo, la linea successiva che verrà valutata sarà this.obj = obj?? Non ho ben chiaro a livello di runtime come viene letto il codice per essere eseguito! E' questo che mi blocca, mi sembra ancora troppo astratto e non mi entra in testa.

  6. #6
    Utente di HTML.it L'avatar di andbin
    Registrato dal
    Jan 2006
    residenza
    Italy
    Messaggi
    18,284
    Originariamente inviato da maninblack
    L'esempio è chiaro, ma quindi dopo la chiamata di wait() del primo metodo, la linea successiva che verrà valutata sarà this.obj = obj?? Non ho ben chiaro a livello di runtime come viene letto il codice per essere eseguito! E' questo che mi blocca, mi sembra ancora troppo astratto e non mi entra in testa.
    Qui ci devono essere almeno 2 thread, uno che invoca inserisci() e l'alto che invoca estrai(). Quindi hai 2 "flussi" di esecuzione ben distinti e separati.
    Ed entrambi i thread devono avere un reference alla stessa istanza di un DepositoOggetto, ovviamente.

    Il thread che vuole inserire invoca inserisci(). Supponi che l'oggetto ci sia e 'disponibile' sia quindi true. Vuol dire che l'oggetto non è ancora stato estratto e quindi l'inseritore si deve bloccare. La condizione nel while è true, quindi esegue il wait(). Il thread inseritore si mette in attesa.

    Ad un certo punto il thread estrattore invoca estrai(). Questo è un altro "flusso" di esecuzione, come detto sopra. Essendo 'disponibile' true, la condizione !disponibile è false, quindi non fa alcun wait e passa dritto a leggere l'oggetto e mettere disponibile=false. Poi fa il notifyAll(). Nota che qui l'altro thread inseritore si "sveglia" ma non può ancora agire subito. Infatti il lock ce l'ha ancora il thread estrattore che deve ancora uscire dal metodo estrai().

    Quando il thread estrattore esce dal metodo estrai() rilascia il lock, allora il thread inseritore potrà acquisire il lock. Appena lo otterrà, potrà continuare nel metodo inserisci(), eseguendo this.obj = obj; ecc....
    Andrea, andbin.devSenior Java developerSCJP 5 (91%) • SCWCD 5 (94%)
    java.util.function Interfaces Cheat SheetJava Versions Cheat Sheet

  7. #7
    Grazie mille per l'esempio. Ora il quadro mi e' molto piu' chiaro. Una cosa pero', quando dici:
    La condizione nel while è true, quindi esegue il wait(). Il thread inseritore si mette in attesa.

    Ad un certo punto il thread estrattore invoca estrai(). Questo è un altro "flusso" di esecuzione, come detto sopra.
    Ma il metodo estrai() deve essere chiamato in precedenza? come fa a partire il secondo thread dopo che il primo si mette in wait() ?? Viene tutto gestito dal main?? non credo sia frutto del fato che il secondo thread parta cosi' a caso

  8. #8
    Utente di HTML.it L'avatar di andbin
    Registrato dal
    Jan 2006
    residenza
    Italy
    Messaggi
    18,284
    Originariamente inviato da maninblack
    Ma il metodo estrai() deve essere chiamato in precedenza?
    No, non è detto/necessario.

    Originariamente inviato da maninblack
    come fa a partire il secondo thread dopo che il primo si mette in wait() ?? Viene tutto gestito dal main??
    Beh sì .... "qualcuno" da qualche parte avrà creato 2 thread. Che poi il thread inseritore arrivi prima lui al inserisci() e il thread estrattore successivamente al estrai() o viceversa .... cambia poco. Tanto sono entrambi bloccanti. Non è possibile inserire se c'è già qualcosa di non estratto e non si può estrarre se non c'è qualcosa da estrarre!!

    Un esempio di creazione dei thread e uso di un DepositoOggetto potrebbe essere:

    codice:
    public class ProvaDepositoOggetti {
        public static void main(String[] args) {
            final DepositoOggetto depObj = new DepositoOggetto();
    
            Runnable inseritore = new Runnable() {
                public void run() {
                    try {
                        for (int i = 0; i < 5; i++) {
                            depObj.inserisci(new Integer(i));
    
                            Thread.sleep(350);
                        }
                    } catch (InterruptedException e) {
                        System.err.println(e);
                    }
                }
            };
    
            Runnable estrattore = new Runnable() {
                public void run() {
                    try {
                        for (int i = 0; i < 5; i++) {
                            Object o = depObj.estrai();
                            System.out.println(o);
    
                            Thread.sleep(540);
                        }
                    } catch (InterruptedException e) {
                        System.err.println(e);
                    }
                }
            };
    
            new Thread(inseritore).start();
            new Thread(estrattore).start();
        }
    }
    Nell'esempio, tecnicamente i due Runnable sono anonymous class (ma si poteva farlo anche ben diversamente), che accedono entrambi a 'depObj', quindi lo stesso oggetto DepositoOggetto.

    I due thread hanno sleep con tempi diversi ma nonostante questo ognuno "attende" l'altro e l'output è 0 1 2 3 4.
    Andrea, andbin.devSenior Java developerSCJP 5 (91%) • SCWCD 5 (94%)
    java.util.function Interfaces Cheat SheetJava Versions Cheat Sheet

  9. #9
    Si ma tecnicamente, leggendo il codice, parte prima il thread inseritore e poi l'estrattore. Il thread estrattore partira' solo quando il thread inseritore incontrera' la chiamata a wait() nel proprio metodo.....Giusto? O i thread che invocano start partono insieme?

  10. #10
    Utente di HTML.it L'avatar di andbin
    Registrato dal
    Jan 2006
    residenza
    Italy
    Messaggi
    18,284
    Originariamente inviato da maninblack
    Si ma tecnicamente, leggendo il codice, parte prima il thread inseritore e poi l'estrattore.
    Nel codice è chiaro che devi invocare start() prima su uno e poi sull'altro.
    Ma l'ordine con cui i run() inizieranno la loro esecuzione non dipende dall'ordine dei start(). Insomma, non è garantito un ordine solo perché hai fatto start() su uno e poi sull'altro. Dipende dallo scheduler.
    L'unica cosa che start() fa è mettere il thread in stato "runnable". Vuol dire che può essere schedulato e quindi passare in "running" ma quando questo passaggio (da runnable a running) avverrà sono cavoli dello scheduler.

    Originariamente inviato da maninblack
    Il thread estrattore partira' solo quando il thread inseritore incontrera' la chiamata a wait() nel proprio metodo.....Giusto? O i thread che invocano start partono insieme?
    Ripeto che quando i run() inizieranno la loro esecuzione, se prima uno o l'altro non è deterministico. Dipende dallo scheduler.
    I thread partono sicuramente prima o poi ad un certo punto, per conto loro. Non perché l'altro ha fatto una wait().
    La wait() è dopo, nel senso che se vedi c'è un try, poi il for, poi la invocazione a inserisci o estrai ecc... I thread iniziano ad eseguire man mano i run e quando, in un inserisci o estrai, beccano la condizione tale che fa entrare nel wait ... beh, allora e solo lì si mettono in attesa.
    Andrea, andbin.devSenior Java developerSCJP 5 (91%) • SCWCD 5 (94%)
    java.util.function Interfaces Cheat SheetJava Versions Cheat Sheet

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.