Uso dei Lock()

di il
6 risposte

Uso dei Lock()

Come faccio a capire se un metodo ha bisogno di un Lock().Nello specifico molte volte inserisco Lock dove nn ci vorrebbe.
Ho scritto questa classe di Prova provando a vedere se mettere o no il Lock nel metodo getincr() apportasse cambiamenti al mio output ma niente.
Qualcuno può aiutarmi??



class Counter{

int counter;

public ReentrantLock lock=new ReentrantLock();

se la variabile è visibile a più Thread i metodi in lettura e in scrittura vanno sync?
public void incr() {
lock.lock();
try {
counter++;
} finally {
lock.unlock();
}
}

public void decr() {
lock.lock();
try {
counter--;
} finally {
lock.unlock();
}
}


public int getincr() {
return counter;
}
}



public class Worker extends Thread {

private Counter c;

public Worker (Counter c) {
this.c=c;
}

enter code here

public void run() {

System.out.println(Thread.currentThread().getName());

for(int i=1;i<=10;i++) {
c.incr(); System.out.println(c.getincr());//this is the method
c.decr(); System.out.println(c.getincr());
}

try {
Thread.currentThread().sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}


//Main
public class Test {

public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
Counter c=new Counter();

Worker[] threads = new Worker[100];

for(int i=0;i<100;i++) {
threads=new Worker(c);
threads.start();
//threads.join();
}
}
}

6 Risposte

  • Re: Uso dei Lock()

    I lock NON SI METTONO a capocchia.

    Per sapere dove metterli bisona avere CHIARO il concetto di ""accesso concorrente a risorsa"", di ""sincronizzazione tra thread""". di ""attesa per l'acquisizione di una risorsa""", ""mutua esclusione"", ecc.

    Sono tutti concetti che hanno a che fare con la programmazione concorrente.

    In pratica, ti serve la TEORIA!

    Se si potesse risolvere l'argomento con un post, non servirebbero interi corsi universitari

    .
  • Re: Uso dei Lock()

    lapolopo ha scritto:


    Come faccio a capire se un metodo ha bisogno di un Lock().
    Mettiamola un po' in generale, in ambito multi-threading la "sincronizzazione" tra thread serve principalmente in uno scenario molto importante: quando due (o più) thread devono accedere concorrentemente ad uno stato "mutabile" (in Java lo stato, cioè i dati, stanno principalmente negli oggetti, ovvero nelle variabili "di istanza" degli oggetti).
    Se invece un oggetto non ha stato o ha stato immutabile, se non ci sono altre questioni (che bisognerebbe precisare e indagare), allora di norma è intrinsecamente thread-safe e la sincronizzazione non serve.

    lapolopo ha scritto:


    Nello specifico molte volte inserisco Lock dove nn ci vorrebbe.
    Ho scritto questa classe di Prova provando a vedere se mettere o no il Lock nel metodo getincr() apportasse cambiamenti al mio output ma niente.
    Innanzitutto un conto è usare il lock intrinseco degli oggetti (con la parola chiave synchronized), un altro conto è usare i lock "espliciti" forniti dal framework.

    Tornando al tuo codice: la tua classe Counter contiene "stato" mutabile, modificabile da più thread. Quindi qui la sincronizzazione (locking) SERVE.

    Ora, faccio solo una precisazione prima. I lock non servono solo a rendere un blocco di codice "atomico", garantendo la "mutua esclusione" tra thread. Servono anche ad un'altra cosa: a garantire la "visibilità" delle modifiche agli altri thread. Ci sarebbe un discorso un po' più lungo da fare ma prendilo così per ora. In sostanza: ANCHE il getincr() deve usare il lock!

    Stai cercando di vedere se l'output cambia oppure no. Bene, ti propongo invece questo codice. Riprendo il tuo Counter (corretto nel getincr()):
    class Counter {
        private int counter;
        private ReentrantLock lock = new ReentrantLock();
    
        public void incr() {
            lock.lock();
            try {
                counter++;
            } finally {
                lock.unlock();
            }
        }
    
        public void decr() {
            lock.lock();
            try {
                counter--;
            } finally {
                lock.unlock();
            }
        }
    
        public int getincr() {
            lock.lock();
            try {
                return counter;
            } finally {
                lock.unlock();
            }
        }
    }

    E poi ti propongo un main() più piccolo e semplificato:
    import java.util.concurrent.locks.ReentrantLock;
    
    public class ProvaCounter {
        public static void main(String[] args) throws InterruptedException {
            final Counter c = new Counter();
    
            Thread[] threads = new Thread[20];
    
            for (int i = 0; i < threads.length; i++) {
                Runnable r = new Runnable() {
                    public void run() {
                        for (int n = 0; n < 10000; n++) {
                            c.incr();
                        }
                    }
                };
                threads[i] = new Thread(r);
                threads[i].start();
            }
    
            // DEVE attendere la terminazione di TUTTI i thread PRIMA di poter stampare il counter!
            for (Thread t : threads) {
                t.join();
            }
    
            System.out.println("counter = " + c.getincr());
        }
    }
    Ora, qui ci sono 20 thread e ciascuno incrementa di 10000. Quindi a rigor di logica il risultato complessivo dovrebbe essere 200000. Prova quante volte vuoi il codice, otterrai sempre

    counter = 200000

    Adesso invece togli (o "commenta") tutti i lock() e unlock() in Counter. Poi riprova il ProvaCounter diciamo anche solo 10 volte e dimmi che output ottieni ...

    Nota che nei thread NON ho messo alcun sleep(). C'è un motivo: uno sleep di 500ms come hai fatto tu rende più improbabile (ma non impossibile ovviamente) il fatto che due thread si "contendano" il lock, semplicemente perché stanno molto di più in sospensione piuttosto che "scocciarsi" a vicenda per acquisire il lock.

    Tutto chiaro ora??
  • Re: Uso dei Lock()

    Ciao grazie per avermi risposto
    L'esempio che hai fatto mi è chiarissimo e so per certo che togliendo i Lock a tutti i metodi le cose cambieranno un po come la classe di Esempio "Conto Bancario" .
    Quello che proprio non riesco a capire è perché il metodo get() con o senza lock non porta nessun cambiamento al output.
  • Re: Uso dei Lock()

    Migliorabile grazie per la tua Risposta.
    So di non avere chiare ancora molte cose per questo ho chiesto un confronto con qualcuno che ne capisce senza dubbio più di me.
    Per capire molte delle cose "Teoriche" hai bisogno del riscontro pratico che in questo e in altri esempi io non trovo.
  • Re: Uso dei Lock()

    lapolopo ha scritto:


    Quello che proprio non riesco a capire è perché il metodo get() con o senza lock non porta nessun cambiamento al output.
    Nel caso specifico del MIO esempio sopra, il getincr() potrebbe anche NON usare il lock. Per un motivo molto semplice: il getincr() l'ho usato solo dopo tutti i join().
    Esiste una questione sul join(), non è purtroppo documentata nel javadoc di Thread ma è descritta bene nel Java Language Specification ( https://docs.oracle.com/javase/specs ), sezione §17.4.5 Happens-before Order

    * All actions in a thread happen-before any other thread successfully returns from a join() on that thread.

    Ovvero, tradotto: tutto quello che un thread X ha fatto come modifiche ad uno "stato", è GARANTITO (per la regola del happens-before, "accade prima") che sarà visibile ad un altro thread Z che ha invocato threadX.join(). Detto ancora in altro modo: il join() rappresenta un ulteriore punto di "sincronizzazione" tra thread. Quindi, ripeto nel caso del mio esempio, il getincr() potrebbe anche non usare il lock.
    Ma in generale e per qualunque altro scenario, getincr() DEVE usare il lock.
  • Re: Uso dei Lock()

    Andbin grazie ancora
    Sapevo del join() se noti nel mio esempio è commentato .Da ora in poi ho la consapevolezza che se ho a che fare con mutabili nel metodo get inserirò il Lock
Devi accedere o registrarti per scrivere nel forum
6 risposte