Thread: start, wait, notify e synchronized

di il
5 risposte

Thread: start, wait, notify e synchronized

Ciao a tutti ragazzi/e , vi scrivo perchè ho un problema con i thread in Java, soprattutto con le parole chiave start, wait, notify e synchronized.

class ThreadB extends Thread
{
    int total;
    @Override
    public void run()
    {
        synchronized(this)
        {
            for(int i=0; i<100 ; i++)
            {
                total += i;
            }
            notify();
        }
    }
}

public class ThreadA 
{
    public static void main(String[] args)
    {
        ThreadB b = new ThreadB();
        b.start(); // avvio il thread b
 
        synchronized(b)
        {
            try
            {
                System.out.println("Waiting for b to complete...");
                b.wait();
            }
            
            catch(InterruptedException e)
            {
                e.printStackTrace();
            }
 
            System.out.println("Total is: " + b.total);
        }
    }
}

Il primo problema risiede nel metodo start. So dalla teoria che quando avvio un nuovo thread , con start, viene invocato il metodo "run" invece qui non succede, o meglio viene prima stampato "Waiting for b to complete..." e poi entra nella regione sincronizzata del metodo run.

synchronized(this) ==> dovrebbe essere che il thread che arriva in questo blocco ("b" in questo caso?) ottiene il lock di questa regione critica e fa le sue operazioni, ma non capisco perchè c'è "this", cosa vuol dire in questo caso?

b.wait(); ==> b aspetta che un altro thread finisca? Non mi è chiaro, so che c'è almeno un thread in ogni programma (che è quello del main)
notify(); ==> dovrebbe servire per risvegliare un thread (che non so quale sia)

Potete chiarirmi le idee?

Grazie delle eventuali risposte.

5 Risposte

  • Re: Thread: start, wait, notify e synchronized

    Escher ha scritto:


    soprattutto con le parole chiave start, wait, notify e synchronized.
    synchronized è una parola chiave. start/wait/notify NO, sono nomi di metodi (identificatori) scelti da chi ha fatto il framework. Ma non sono nomi "riservati".

    Escher ha scritto:


    Il primo problema risiede nella parola chiave start. So dalla teoria che quando avvio un nuovo thread , con start, viene invocato il metodo "run" invece qui non succede, o meglio viene prima stampato "Waiting for b to complete..." e poi entra nella regione sincronizzata del metodo run.

    synchronized(this) ==> dovrebbe essere che il thread che arriva in questo blocco ("b" in questo caso?) ottiene il lock di questa regione critica e fa le sue operazioni, ma non capisco perchè c'è "this", cosa vuol dire in questo caso?

    b.wait(); ==> b aspetta che un altro thread finisca? Non mi è chiaro, so che c'è almeno un thread in ogni programma (che è quello del main)
    notify(); ==> dovrebbe servire per risvegliare un thread (che non so quale sia)

    Potete chiarirmi le idee?
    C'è una "piccola" questione. lo start() su un Thread mette solo il thread in stato "runnable". Il run() NON lo invoca o fa invocare subito. Dipende dallo scheduler dei thread quando effettivamente il run() inizierà la esecuzione.

    Questo vuol dire che è possibile che il run() inizi prima, acquisisca il lock e arrivi a fare il notify() (se non c'è nessuno in wait non succede nulla) e poi dopo il synchronized(b) faccia il suo corso. Oppure è possibile il contrario.

    Se vuoi solo attendere la terminazione di un thread, semplicemente si usa join() sul java.lang.Thread.
  • Re: Thread: start, wait, notify e synchronized

    Hai fatto un bel casino

    1) il metodo "start()" fa partire il thread, MA NON ALLA CHIAMATA ESATTA DELLO "start()", ma PRIMA O POI, quando ha voglia lui. QUANDO, NON E' SOTTO IL TUO CONTROLLO

    2) nel "main" tu ti sincronizzi con "b" e in "b" tu ti sincronizzi con "this": cioe' hai DUE THREAD separati che si SINCRONIZZANO sullo stesso oggetto di sincronizzazione.

    La synchronized fa si CHE SOLO UN THREAD alla volta puo' accedere all'oggetto su cui fai la sincronizzazione, e devi MOLLARLO (uscire dal blocco synchronized(){ } ) affiche un'altro thread (NON ESATTAMENTE QUANDO LO MOLLI, ma DOPO UN PO', a SUA TOTALE DISCREZIONE, NON E' SOTTO IL TUO CONTROLLO QUANDO) possa acquisirlo per la nuova sincronizzazione

    synchronized(){ } implementa il MUTEX: Mutual Exclusion.


    La wait/notify/notifyAll sono altre due primitive che ti permettono di DECIDERE quando bloccare IL THREAD CORRENTE e quando far ripartire UN'ALTRO THREAD.

    Per capire come funziona devi implementare il concetto di PRODUTTORE/CONSUMATORE:

    1) due thread, uno che produce qualcosa (anche una banale stringa con scritto "1", "2", ...)
    2) uno che consuma le stringhe prodotte dal produttore
    3) una CODA a DIMENSIONE LIMITATA, in cui il produttore inserisce quello che ha prodotto, ed il consumatore consuma quello che trova

    Il produttore puo' produrre e inserire nella coda SOLO SE LA CODA NON E' PIENA
    Il consumatore puo' consumare il contenuto della cosa SOLO SE LA CODA NON E' VUOTA

    Aggiungi poi delle sleep random tra 1 e 5 secondi tra una produzione e l'altra, e tra un consumo e l'altro, e vedrai che NECCESSARIAMENTE devi:

    1) SOSPENDERE il produttore se la coda e ' piena, e riattuvarlo quando il consumatore ha consumato ALMENO un elemento dalla coda
    2) SOSPENDERE il consumatore se la cda e' vuota e riattivarlo quando il produttore ha inserito ALMENO un elemento nella coda.

    Nota: Il produttore NON DEVE SAPERE della presenza del consumatore, ed il CONSUMATORE non deve sapere della presenza del produttore.
    ENTRAMBI, pero' SANNO della presenza della coda

    La "object.notify()" fa partire UNO DEI THREAD che sono in attesa (hanno chiamato la "object.wait()") sul QUELL'OGGETTO. NON SAI QUALE, NON E' SOTTO IL TUO CONTROLLA SAPERE QUALE e se ci sono thread in attesa!

    E' ovvio, pero' che TU SAI quali thread (NON specificatamente, ma in generale) usano quell'oggetto per la sincronizzazione: il codice lo hai scritto TU, quindi non c'e' nulla di misterioso/miracoloso.

    Consiglio: quando ti serve un oggetto per sincronizzare dei thread, USA SEMPRE un oggetto DEDICATO, ed usato SOLO ED ESCLUSIVAMENTE per quello scopo. Non e' la panacea di tutti i mali, ma nel 90% dei casi eviti i 'deadlock', la bestia nera della programmazione multithreading
  • Re: Thread: start, wait, notify e synchronized

    synchronized è una parola chiave. start/wait/notify NO, sono nomi di metodi (identificatori) scelti da chi ha fatto il framework. Ma non sono nomi "riservati".
    Hai ragione, errore mio, correggo.

    Correggetemi se sbaglio, ancora non ho chiari alcuni punti.
    C'è una "piccola" questione. lo start() su un Thread mette solo il thread in stato "runnable". Il run() NON lo invoca o fa invocare subito. Dipende dallo scheduler dei thread quando effettivamente il run() inizierà la esecuzione.
    Grazie. Per capire cosa eseguisse per primo, ho messo delle stampe in alcuni punti del codice e ho notato che il metodo "run" lo eseguiva (ripetendo l'esecuzione più volte) sempre dopo la stampa di "Waiting for b to complete...". Quindi dopo aver messo il thread nello stato "runnable" con "b.start()", viene seguita la parte di codice "synchronized(b)" che vuol dire che il thread "b" ha preso il lock per quella regione critica. Quindi stampa "Waiting for b to complete...". A questo punto "b" decide di aspettare che il thread (è sempre b?) finisca di eseguire il metodo "run" (che lo scheduler gli ha detto quando eseguirlo). Infine si notifica che il thread (quello che ha eseguito "run") ha concluso.


    nel "main" tu ti sincronizzi con "b" e in "b" tu ti sincronizzi con "this": cioe' hai DUE THREAD separati che si SINCRONIZZANO sullo stesso oggetto di sincronizzazione.
    Quindi ho 2 thread con lo stesso nome? (b)

    Per capire come funziona devi implementare il concetto di PRODUTTORE/CONSUMATORE
    Credo che implementerò qualcosa del genere se mi fa capire bene.

    Grazie.
  • Re: Thread: start, wait, notify e synchronized

    Escher ha scritto:


    Grazie. Per capire cosa eseguisse per primo, ho messo delle stampe in alcuni punti del codice e ho notato che il metodo "run" lo eseguiva (ripetendo l'esecuzione più volte) sempre dopo la stampa di "Waiting for b to complete...". Quindi dopo aver messo il thread nello stato "runnable" con "b.start()", viene seguita la parte di codice "synchronized(b)" che vuol dire che il thread "b" ha preso il lock per quella regione critica. Quindi stampa "Waiting for b to complete...". A questo punto "b" decide di aspettare che il thread (è sempre b?) finisca di eseguire il metodo "run" (che lo scheduler gli ha detto quando eseguirlo). Infine si notifica che il thread (quello che ha eseguito "run") ha concluso.
    Il punto è che il tuo codice funziona SOLO se il "main" thread (è quello che esegue il main, lo crea la JVM, non tu) arriva a fare il synchronized(b) e b.wait() PRIMA che il tuo run() di ThreadB arrivi a fare il notify().

    Però è teoricamente ma assolutamente possibile che (a seconda di piattaforma/S.O.) le cose vadano diversamente, es. il run() completa PRIMA che il "main" thread arrivi a fare il b.wait(). In tal caso il main thread rimane inchiodato lì e basta.

    Escher ha scritto:


    Quindi ho 2 thread con lo stesso nome? (b)
    NO. Di thread ce ne sono in effetti 2: il "main" thread (in cui la JVM fa partire il main() ) e il tuo thread ThreadB.
    Ma di oggetto ThreadB ne hai 1 solo (hai fatto un solo new ThreadB() ). Nel main lo assegni a 'b' e all'interno di ThreadB usi this ma l'oggetto è lo stesso.

    Escher ha scritto:


    Credo che implementerò qualcosa del genere se mi fa capire bene.
    Se devi solo aspettare la terminazione di un thread (es. per ottenere dei risultati), allora lo ripeto: join()
  • Re: Thread: start, wait, notify e synchronized

    Il punto è che il tuo codice funziona SOLO se il "main" thread (è quello che esegue il main, lo crea la JVM, non tu) arriva a fare il synchronized(b) e b.wait() PRIMA che il tuo run() di ThreadB arrivi a fare il notify().

    Però è teoricamente ma assolutamente possibile che (a seconda di piattaforma/S.O.) le cose vadano diversamente, es. il run() completa PRIMA che il "main" thread arrivi a fare il b.wait(). In tal caso il main thread rimane inchiodato lì e basta.

    NO. Di thread ce ne sono in effetti 2: il "main" thread (in cui la JVM fa partire il main() ) e il tuo thread ThreadB.
    Ma di oggetto ThreadB ne hai 1 solo (hai fatto un solo new ThreadB() ). Nel main lo assegni a 'b' e all'interno di ThreadB usi this ma l'oggetto è lo stesso.
    Ora mi è tutto molto più chiaro!
    Se devi solo aspettare la terminazione di un thread (es. per ottenere dei risultati), allora lo ripeto: join()
    Grazie ma era solo un esempio preso sul web e che non riuscivo a capire cosa/come facesse, grazie comunque !

    Grazie ancora.
Devi accedere o registrarti per scrivere nel forum
5 risposte