Esercizio Classe thread-safe

di il
15 risposte

Esercizio Classe thread-safe

Salve a tutti, sto preparando un esame universitario e chiedo il vostro aiuto per una tipologia di esercizio che spesso mi manda in confusione. Eccone un esempio:
"La classe StringQuiz consente a diversi thread di tentare di indovinare una stringa segreta, entro un tempo prestabilito. Il costruttore accetta la stringa segreta e un timeout in millisecondi. Il metodo guess accetta una stringa e restituisce vero se è uguale a quella segreta e falso altrimenti. Ciascun thread ha 3 possibilità di indovinare, oltre le quali il metodo guess lancia un'eccezione. Scaduto il timeout, il metodo guess lancia un'eccezione ogni volta che viene invocato. La classe dev'essere thread-safe."

Premetto che è un esame scritto e non al compilatore, ecco perchè lo scopo dell'esercizio è creare questa classe, indipendentemente dal main o dai thread che la instanzieranno.
La mia idea è quella di scrivere il metodo guess come un metodo sincronizzato, ma questo basta a rendere la classe thread-safe? O c'è bisogno di una variabile volatile booleana?

//Perdonate l'eventuale stupidità delle mie domande

15 Risposte

  • Re: Esercizio Classe thread-safe

    Assioma: le domande non sono mai stupide. Caso mai lo sono le risposte

    Stai facendo pasticci!

    Ragiona: tu istanzi una classe che ha un setter ed un getter.

    L'istanza la dai in pasto a 100 thread che la usano SOLO in lettura, (non c'e' NESSUNO che la usa in scrittura).

    I metodi della classe devono essere synchronyzed?

    Ed ora supponiamo che ci sia SOLO un thread che OGNI tanto la usa in SCRITTURA: i getter ed i setter devono essere synchronyzed?

    E questa e' la prima parte delle domande
    Se rispondi bene, ti posto le altre
  • Re: Esercizio Classe thread-safe

    violet_prog ha scritto:


    scrivere il metodo guess come un metodo sincronizzato, ma questo basta a rendere la classe thread-safe?
    Mettere un metodo di istanza synchronized vuol dire innanzitutto che se più thread cercano di invocare quel metodo su uno STESSO oggetto, allora ci sarà una mutua-esclusione, poiché per eseguire il metodo bisogna acquisire il lock su quell'oggetto, che può essere fatto da un solo thread per volta.

    Ma il punto non è tanto questo. Ci sono altre questioni più oscure su quanto hai detto:
    - "un timeout in millisecondi". Questo timeout da quando parte?
    - "ciascun thread ha 3 possibilità". Chi controlla il numero di possibilità? Cioè, dove è rinforzato questo vincolo?
  • Re: Esercizio Classe thread-safe

    migliorabile ha scritto:


    Ragiona: tu istanzi una classe che ha un setter ed un getter.

    L'istanza la dai in pasto a 100 thread che la usano SOLO in lettura, (non c'e' NESSUNO che la usa in scrittura).

    I metodi della classe devono essere synchronyzed?
    No, perchè se non modifica i campi non mi interessa sincronizzare il metodo.
    Ed ora supponiamo che ci sia SOLO un thread che OGNI tanto la usa in SCRITTURA: i getter ed i setter devono essere synchronyzed?
    Neanche.


    Non so se ho risposto bene, ma in ogni caso non mi ha molto aiutato a ragionare sull'esercizio al momento ahhahah
  • Re: Esercizio Classe thread-safe

    andbin ha scritto:


    Ma il punto non è tanto questo. Ci sono altre questioni più oscure su quanto hai detto:
    - "un timeout in millisecondi". Questo timeout da quando parte?
    - "ciascun thread ha 3 possibilità". Chi controlla il numero di possibilità? Cioè, dove è rinforzato questo vincolo?

    Ehm... questo è proprio quello che mi chiede di fare l'esercizio. Sono io che devo scrivere una classe che gestisca queste situazioni, quello che ho postato virgolettato è la traccia dell'esercizio. La mia domanda è solo circa la garanzia della classe thread-safe.
  • Re: Esercizio Classe thread-safe

    violet_prog ha scritto:


    La mia domanda è solo circa la garanzia della classe thread-safe.
    Prendiamo la gestione dei tentativi con limite massimo (es. 3): puoi gestirlo all'interno della classe StringQuiz? (io ti dico di sì). Puoi farlo senza dover conoscere a priori quali/quanti thread andranno ad usare quell'oggetto StringQuiz? (ti dico di nuovo di sì).

    E se hai tutto questo gestito in StringQuiz, vuol dire che qualcosa di "mutabile" ce l'hai, pertanto mettere il metodo guess come synchronized è il MINIMO che tu possa fare.
  • Re: Esercizio Classe thread-safe

    Purtroppo ho veramente difficoltà su questa parte del programma. Perciò cose che sono ovvie, per me non lo sono per niente
    Quindi cerco di seguirti passo passo...
    Prendiamo la gestione dei tentativi con limite massimo (es. 3): puoi gestirlo all'interno della classe StringQuiz? (io ti dico di sì).
    posso gestirla utilizzando una variabile count che incremento nel metodo guess quando la stringa di input è diversa da "quella segreta" ?
    E se hai tutto questo gestito in StringQuiz, vuol dire che qualcosa di "mutabile" ce l'hai, pertanto mettere il metodo guess come synchronized è il MINIMO che tu possa fare.
    E' il minimo, e quindi ritorno alla mia domanda di partenza... E non basta? O con minimo intendi, perchè in più utilizzo la gestione del contatore?!
  • Re: Esercizio Classe thread-safe

    violet_prog ha scritto:


    ...
    posso gestirla utilizzando una variabile count che incremento nel metodo guess quando la stringa di input è diversa da "quella segreta" ?
    Ti basta UNA SOLA variabile count? O una PER THREAD?
    Supponi di avere 3 thread che provano a giocare al quiz!
    E se ne hai 10 o 100 o 1000?
  • Re: Esercizio Classe thread-safe

    migliorabile ha scritto:


    violet_prog ha scritto:


    ...
    posso gestirla utilizzando una variabile count che incremento nel metodo guess quando la stringa di input è diversa da "quella segreta" ?
    Ti basta UNA SOLA variabile count? O una PER THREAD?
    Supponi di avere 3 thread che provano a giocare al quiz!
    E se ne hai 10 o 100 o 1000?
    Una per thread! La butto lì... Se è una cavolata siate comprensivi ahahhaa
    Se all'interno della classe uso un HashMap<Thread,Integer> map, dove ad ogni thread corrisponde il valore di count che vado ad aggiornare quando occorre ....... Riesco a gestire questa situazione nel metodo guess, all'interno di un blocco synchronized(map)?
  • Re: Esercizio Classe thread-safe

    violet_prog ha scritto:


    un HashMap<Thread,Integer> map, dove ad ogni thread corrisponde il valore di count
    Esatto. Proprio qui volevo farti arrivare.

    violet_prog ha scritto:


    Riesco a gestire questa situazione nel metodo guess, all'interno di un blocco synchronized(map)?
    Il fatto di mettere synchronized sul metodo (cosa semplice ma drastica) o magari solo su un blocco di poche istruzioni dentro il metodo ... è un dettaglio implementativo. Dipende insomma da come implementi tutta la logica. Quindi il consiglio è: dimentica per un attimo la thread-safety e implementa la logica pensando ad un singolo thread. Quando avrai la visione chiara del tutto, se ragionerai bene (es. ti domandi "che succede se qui in questo punto ci passano 2 thread contemporaneamente?") allora capirai dove mettere il/i synchronized.
  • Re: Esercizio Classe thread-safe

    public class StringQuiz {
        
        Map<Thread,Integer> map;
        String stringa;
        long timeOut;
        
        StringQuiz(String s, long time){
            map=new HashMap<Thread,Integer>();
            stringa=s;
            timeOut=time+System.currentTimeMillis();
        }
        
        public synchronized boolean guess(String s){
            if(timeOut-System.currentTimeMillis()>0){
                if(stringa.equals(s))
                    return true;
                Thread t=Thread.currentThread();
                map.put(t, map.get(t)+1);
                if(map.get(t)>=3)
                    throw new RuntimeException("Numero tentativi superato");
                }
            else 
                throw new RuntimeException("TimeOut!!!!");
            return false;
        }
    Qualcosa del genere, come andrebbe...?
  • Re: Esercizio Classe thread-safe

    violet_prog ha scritto:


    Qualcosa del genere, come andrebbe...?
    Sì e no ... quasi. Innanzitutto, lo dico in generale, abituati a tenere sempre i campi come "private". Salvo casi e design particolari.

    Io farei

    if (System.currentTimeMillis() < timeOut)

    che è anche più comprensibile ("se sono prima del timeout"). Piuttosto di dover ragionare sulla sottrazione.

    Ma il vero problema (e qui ti si schianta) è che

    map.get(t)+1

    all'inizio, la prima volta per il thread, il get ti dà null perché non c'è la chiave. Un Integer + int causa il auto-unboxing del Integer prima di fare la somma ma essendo un reference null ti becchi NullPointerException. Cerca di sistemarlo con qualche logica in più, cercando di non ripetere il get.
  • Re: Esercizio Classe thread-safe

    public synchronized boolean guess(String s){
            if(System.currentTimeMillis()<timeOut){
                if(stringa.equals(s))
                    return true;
                Thread t=Thread.currentThread();
                Integer i=map.get(t);
                if(i==null)
                    map.put(t, 1);
                else
                    map.put(t, i++);
                if(map.get(t)>=3)
                    throw new RuntimeException("Numero tentativi superato");
                }
            else 
                throw new RuntimeException("TimeOut!!!!");
            return false;
        }
  • Re: Esercizio Classe thread-safe

    violet_prog ha scritto:


                Integer i=map.get(t);
                if(i==null)
                    map.put(t, 1);
                else
                    map.put(t, i++);
                if(map.get(t)>=3)
                    throw new RuntimeException("Numero tentativi superato");
                }
    "quasi" corretto. Ti stai dimenticando che i++ vuol dire che prima usa il valore e poi dopo lo incrementa. E il "usa" significa che quel valore che ha in quel momento lo usa per passarlo al put.
    In questi casi è più chiaro e semplice i+1 (o in alternativa ++i)
  • Re: Esercizio Classe thread-safe

    andbin ha scritto:


    In questi casi è più chiaro e semplice i+1 (o in alternativa ++i)
    modificando questa cosa, va bene?
    public synchronized boolean guess(String s){
            if(System.currentTimeMillis()<timeOut){
                if(stringa.equals(s))
                    return true;
                Thread t=Thread.currentThread();
                Integer i=map.get(t);
                if(i==null)
                    map.put(t, 1);
                else{
                    map.put(t, i+1);
                    if(map.get(t)>=3)
                        throw new RuntimeException("Numero tentativi superato");
                    }
            }
            else 
                throw new RuntimeException("TimeOut!!!!");
            return false;
        }
Devi accedere o registrarti per scrivere nel forum
15 risposte