Esercizio Thread

di il
4 risposte

Esercizio Thread

Salve a tutti, sono nuovo nel forum e ho bisogno del vostro aiuto.
Ho il seguente esercizio:
Realizzare la classe thread-safe Market, che permette a diversi thread di contrattare il prezzo
di un servizio. Il metodo sell ore il servizio per un certo prezzo e blocca il chiamante nché
non arriverà una richiesta con un importo adeguato. Simmetricamente, il metodo buy richiede il
servizio per un certo prezzo e blocca il chiamante nché non arriverà un'oerta adeguata.
Per semplicità, si può supporre che tutti gli importi passati a sell e buy siano diversi.
L'implementazione deve rispettare il seguente esempio d'uso, in cui diversi thread condividono
un oggetto Market m:
Output
Thread 1: m.buy(10.0); resta bloccato
Thread 2: m.sell (15.50); resta bloccato
Thread 3: m.sell (12.0) ; resta bloccato
Thread 4: m.buy(13.0); sblocca T3 e ritorna
Thread 5: m.buy(11.0); resta bloccato
Thread 6: m.sell (9.50) ; sblocca T1 oppure T5 e ritorna
La mia soluzione è la seguente:

import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;

public class  Market {

    List<Double> listBuy;
    List<Double> listSell;

    Map<Thread,Double> mapBuy;
    Map<Thread,Double> mapSell;

    public Market(){
        listBuy = new ArrayList<>();
        listSell = new ArrayList<>();

        mapBuy = new HashMap<>();
        mapSell = new HashMap<>();
    }

    public synchronized void buy(Double b){
        try {
            if (mapSell.isEmpty()) {
                mapBuy.put(Thread.currentThread(),b);
                System.out.println(Thread.currentThread().getName() + " resta bloccato");
                this.wait();
//                System.out.println("Sblocca " + Thread.currentThread().getName() + " e ritorna");
//                mapBuy.remove(Thread.currentThread());
//                return;
            }else{
                for(Thread curr : mapSell.keySet()){
                    if(b>mapSell.get(curr)){
                        curr.interrupt();
                        mapSell.remove(curr);
                        return;
                    }
                }
                mapBuy.put(Thread.currentThread(),b);
                System.out.println(Thread.currentThread().getName() + " resta bloccato");
                this.wait();
//                System.out.println("Sblocca " + Thread.currentThread().getName() + " e ritorna");
//                mapBuy.remove(Thread.currentThread());
//                return;
            }
        }catch(InterruptedException e){
            System.out.println("Sblocca " + Thread.currentThread().getName() + " e ritorna");
            mapBuy.remove(Thread.currentThread());
            return;

        }
    }
    public synchronized void sell(Double s){
        try{
            if(mapBuy.isEmpty()){
                mapSell.put(Thread.currentThread(),s);
                System.out.println(Thread.currentThread().getName() + " resta bloccato");
                this.wait();
//                System.out.println("Sblocca " + Thread.currentThread().getName() + " e ritorna");
//                mapSell.remove(Thread.currentThread());
//                return;
            }else{
                for(Thread curr : mapBuy.keySet()){
                    if(s<mapBuy.get(curr)){
                        curr.interrupt();
                        mapBuy.remove(curr);
                        return;
                    }
                }
                mapSell.put(Thread.currentThread(),s);
                System.out.println(Thread.currentThread().getName() + " resta bloccato");
                this.wait();
//                System.out.println("Sblocca " + Thread.currentThread().getName() + " e ritorna");
//                mapSell.remove(Thread.currentThread());
            }
        }catch(InterruptedException e){
            System.out.println("Sblocca " + Thread.currentThread().getName() + " e ritorna");
            mapSell.remove(Thread.currentThread());
            return;
        }
    }

    public static void main(String[] args) {

        Market m = new Market();

        Thread myThread1 = new Thread("T1"){
            @Override
            public void run() {
                m.buy(10.0);
            }
        };
        Thread myThread2 = new Thread("T2"){
            @Override
            public void run() {
                m.sell(15.50);
            }
        };
        Thread myThread3 = new Thread("T3"){
            @Override
            public void run() {
                m.sell(12.0);
            }
        };
        Thread myThread4 = new Thread("T4"){
            @Override
            public void run() {
                m.buy(13.0);
            }
        };
        Thread myThread5 = new Thread("T5"){
            @Override
            public void run() {
                m.buy(11.0);
            }
        };
        Thread myThread6 = new Thread("T6"){
            @Override
            public void run() {
                m.sell(9.50);
            }
        };

        

        myThread1.start();
        myThread2.start();
        myThread3.start();
        myThread4.start();
        myThread5.start();
        myThread6.start();
    }

}


Il mio problema che l'output non compare in ordine. Ossia è meglio dire che ogni singolo thread non entra in ordine. Sono due giorni che sto sbattendo la testa. Aiutatemi

4 Risposte

  • Re: Esercizio Thread

    MaxiPrime ha scritto:


    Il mio problema che l'output non compare in ordine. Ossia è meglio dire che ogni singolo thread non entra in ordine.
    Partiamo da un aspetto, la implementazione fatta è purtroppo palesemente errata tecnicamente. Il wait() va sempre messo in un ciclo che deve testare una condizione che è quella che deve "reggere" affinché il thread debba rimanere in wait.
    while (condizione_per_far_stare_in_wait) {
        xyz.wait();
    }
    Inoltre nel tuo codice NON c'è l'uso di notify()/notifyAll(). Questi sono i metodi che si "contrappogono" al wait() e servono appunto per gestire la condition-queue intrinseca degli oggetti.

    Tu hai usato degli interrupt() ma questo uso è sbagliato in questo contesto! Anche solo per una banale/ovvia questione concettuale: se interrompi così un thread, quel thread non ha modo di capire se è stato interrotto perché è la logica del Market che ha deciso così oppure se è stato interrotto perché un (potenziale) qualunque altro thread ha deciso di interrompere per es. tutti i 6 thread.

    Infine c'è la questione dell'ordine di avvio. Purtroppo questo è un aspetto molto aleatorio dei thread. Lo start() fa solo una banalissima cosa: mette il thread in stato "runnable" (ovvero: non in esecuzione ma che può essere eseguito). Poi ci pensa lo scheduler dei thread a decidere quando avviarlo. E qui quindi dipende dallo scheduler dei thread del Sistema Operativo, dal carico di lavoro della CPU in quel momento, dal numero di core .... da millemila fattori che non sono facilmente noti.

    Insomma: NON è garantito che l'ordine di avvio dei thread segua davvero l'ordine degli start(). Il primo buy() eseguito potrebbe essere quello di T4 o T5. O potrebbe essere eseguito per primo il sell di T6.

    Se vuoi che ci sia un ordine ben preciso, si può fare ma richiede ulteriori valutazioni e più codice.
    Pertanto la domanda è: quell'output mostrato è solo un "esempio", uno dei possibili scenari .... o deve sempre essere esattamente quello scenario??


    P.S. per completezza riporto che ho cercato il testo in rete: si tratta di un esame universitario del 2017 ma sul PDF che ho trovato non c'è nulla in più di spiegazione rispetto a quanto già riportato sopra.
  • Re: Esercizio Thread

    Ciao andib, grazie per avermi risposto.
    La mia soluzione che è postata è una delle più versioni che ho in serbo e che assolutamente rispettano le regole che mi hai elencato. Ho dovuto fare così per cercare di soddisfare le richieste e l'output della traccia, che è rigorosamente voluta così dal mio professore di Java avanzata. Ciò che hai trovato su internet è la pagina del mio professore con la quale devo fare l'esame e sto facendo tutti gli esercizi sui thread che compaiono sul pdf.
    Provo a spiegarmi:
    Usare notify() o ancora peggio notifyAll() risveglierebbe una qualsiasi, o tutti, Thread in stato di attesa violando così il contratto. Ho preferito, o non sono riuscito, a trovare un modo migliore se non catturare il lancio dell'InterruptedException. E fin qui non posso dire che non vada bene anche se non sembra fatta bene tecnicamente.
    Se provassi a compilare il codice, l'output richiesto compare ma mi sono incappato sul problema che non riesco a gestire l'ordine di entrata dei Thread nel main.
    Io in altri esercizi ho risolto usando una sequenza di join() ma siccome la traccia richiede che il Thread che non trova un prezzo tale che soddisfi il contratto, deve andare in wait() e allora il programma si ferma sul primo Thread.
  • Re: Esercizio Thread

    MaxiPrime ha scritto:


    La mia soluzione che è postata è una delle più versioni che ho in serbo e che assolutamente rispettano le regole che mi hai elencato. Ho dovuto fare così per cercare di soddisfare le richieste e l'output della traccia, che è rigorosamente voluta così dal mio professore di Java avanzata.
    Ho provato il tuo codice, l'ho lanciato 5 volte. E ciascuna volta ha prodotto un output differente. Questo perché, te l'ho già spiegato prima, l'ordine di avvio "materiale" dei thread non è garantito che sia l'ordine degli start().
    Quindi il tuo codice NON può garantire che l'output sia sempre esattamente quello indicato nella traccia.

    Se vuoi quell'ordine, si può fare ma serve "altro" codice da scrivere.

    MaxiPrime ha scritto:


    Usare notify() o ancora peggio notifyAll() risveglierebbe una qualsiasi, o tutti, Thread in stato di attesa violando così il contratto.
    Serve proprio esattamente a quello il while condizionato che racchiude il wait() !!!
  • Re: Esercizio Thread

    Riprovo tenendo conto dei tuoi consigli. Grazie andbin
Devi accedere o registrarti per scrivere nel forum
4 risposte