Ripetizione di Task

di il
18 risposte

Ripetizione di Task

La traccia dell'esercizio è la seguente:
Realizzare la classe PeriodicExecutor, che consente di eseguire una serie di task periodicamente, con un limite al numero di task che possono essere eseguiti contemporaneamente. Il costruttore accetta questo limite. Il metodo addTask accetta un Runnable e un long x, e fa in modo che il Runnable venga eseguito ripetutamente, con un ritardo di x millisecondi tra la fine di un’esecuzione e l’inizio della successiva. Se però (ri)avviare un Runnable porta a superare il limite, l’avvio viene rimandato finché ci sarà lapossibilità di eseguirlo senza violare il limite.Il limite si riferisce al numero di task che stanno eseguendo il loro Runnable, non al periodo durante il quale stanno aspettando il ritardox.L’implementazione deve rispettare il seguente esempio d’uso:
PeriodicExecutor exec= new PeriodicExecutor(2);
Runnable r1=...
Runnable r2=...
Runnable r3=...
exec.addTask(r1,1000);
exec.addTask(r2,500);
exec.addTask(r3,700);


import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class PeriodicExecutor {
    BlockingQueue<Runnable> bq;

    public PeriodicExecutor(int n){
        bq=new ArrayBlockingQueue<>(n);

    }

    public void addTask(Runnable r,long x){
            try {
                bq.put(r);
                new Thread(r).start();
                bq.remove(r);
                Thread.sleep(x);
            } catch (InterruptedException e) {
                return;
            }
    }

    public static void main(String[] args){
        PeriodicExecutor exec= new PeriodicExecutor(2);
        Runnable r1=()->{
            System.out.println("R1");
        };

        Runnable r2=()->{
            System.out.println("R2");
        };

        Runnable r3=()->{
            System.out.println("R3");
        };

        exec.addTask(r1,1000);
        exec.addTask(r2,500);
        exec.addTask(r3,700);
    }
}
sono fermo sulla ripetizione del Task, se aggiungessi while(true) ripeterei solo R1 e gli altri addTask non li andrei mai a riprendere perchè non c'è un rilascio del lock. sleep() non lo rilascia.

18 Risposte

  • Re: Ripetizione di Task

    HalJordan ha scritto:


    sleep() non lo rilascia.
    Quando lo sleep viene eseguito, non c'è alcun lock tenuto! Il locking viene usato internamente dal ArrayBlockingQueue e quindi avviene solo nel put e remove. Ma nel TUO codice non c'è una acquisizione esplicita di un lock.

    HalJordan ha scritto:


    sono fermo sulla ripetizione del Task, se aggiungessi while(true) ripeterei solo R1 e gli altri addTask non li andrei mai a riprendere
    Appunto. Non puoi mettere un ciclo nel addTask.

    La questione quindi diventa: invece di passare il Runnable direttamente al Thread, passi al Thread un ALTRO Runnable (anche benissimo "nascosto", interno a PeriodicExecutor) che USA il Runnable ricevuto da addTask e lo ri-esegue con la logica che hai già fatto.
  • Re: Ripetizione di Task

    Aggiungo attributo di Classe private Runnable runnable e in addTask implemento in questo modo:
    public void addTask(Runnable r,long x){
            runnable=()->{
                while(true){
                    try {
                        bq.put(r);
                        r.run();
                        bq.take();
                        Thread.sleep(x);
    
                    } catch (InterruptedException e) {
                        return;
                    }
                }
            };
            new Thread(runnable).start();
        }

    grazie andbin, ero completamente in palla. Un'altra cosa, esiste un tipo di Collezione Set sincronizzata? In pratica vorrei aggiungere e togliere elementi da due Collezioni in thread-safe.
  • Re: Ripetizione di Task

    HalJordan ha scritto:


    Aggiungo attributo di Classe private Runnable runnable
    No, NON deve essere così! (basta che sia "locale" nel addTask).

    HalJordan ha scritto:


    Un'altra cosa, esiste un tipo di Collezione Set sincronizzata?
    Vector e Hashtable sono le "vecchie" collezioni e sono synchronized (thread-safe). Le altre basilari in java.util no, non lo sono. Ma si possono far passare nei synchronizedXXX() di Collections.

    Quelle in java.util.concurrent invece sono ok per uso concorrente (e quindi hanno già certamente le opportune sincronizzazioni).

    HalJordan ha scritto:


    In pratica vorrei aggiungere e togliere elementi da due Collezioni in thread-safe.
    Se intendi tolgo da una e inserisco nell'altra e tutto questo deve essere "atomico", allora non è questione che la singola collezione sia thread-safe. Devi fare TU in modo che l'insieme di queste due operazioni sia atomico !
  • Re: Ripetizione di Task

    Ho fatto un esempio sbagliato, perchè effettivamente sono operazioni atomiche. Comunque hai risposto su quello che volevo sapere.

    Per arrivare ad una conoscenza totale di Java, oltre agli anni di pratica ed esperienza, che certificati hai preso e che tipo di studi hai percorso? se posso e non sono invadente.
  • Re: Ripetizione di Task

    HalJordan ha scritto:


    che certificati hai preso e che tipo di studi hai percorso? se posso e non sono invadente.
    Puoi vedere nel "Chi sono" sul mio blog.
  • Re: Ripetizione di Task

    Grazie andbin, gentilissimo come sempre.
  • Re: Ripetizione di Task

    Ho due Thread che condividono una Lista. Il primo Thread ogni decimo di secondo aggiunge un numero a tale lista, se è multiplo di 100 il thread termina. Il secondo Thread una volta ogni secondo stampa la somma della lista e termina appena termina il primo thread.

    Cosa dovrei fare?
    Nel Thread 1 prendo il lock di list e faccio un while finchè non trovo un multiplo e metto in wait ogni decimo di secondo.
    Nel Thread 2 un while con condizione !t1.isInterrupter(), perchè voglio fare la somma fino a che thread 1 è attivo e anche qui wait su un secondo.
    cosa sbaglio da questo ragionamento?
    
    public class MyThread{
        public static void main(String[] args){
            List<Integer> list=new LinkedList<>();
    
            Thread t1=new Thread(()->{
               synchronized (list){
                   int i;
                   while((i=new Random().nextInt(200))%10!=0){
                       list.add(i);
                       System.out.println(""+i);
                       try {Thread.currentThread().sleep(1000);}catch(InterruptedException e){ return;}
                   }
                   System.out.println(""+i);
                   Thread.currentThread().interrupt();
               }
            });
    
            Thread t2=new Thread(()->{
                synchronized(list){
                    while(!t1.isInterrupted()) {
                        int sum = 0;
                        for (Integer i : list) {
                            sum = i + sum;
                        }
                        System.out.println("SOMMA: "+sum);
                        try {Thread.currentThread().sleep(10000);}catch(InterruptedException e){ return;}
                    }
                    Thread.currentThread().interrupt();
                }
            });
            t1.start();
            t2.start();
        }
    }
    
  • Re: Ripetizione di Task

    HalJordan ha scritto:


    Cosa dovrei fare?
    Dal momento che LinkedList NON è thread-safe, devi chiaramente prevedere l'acquisizione di un lock in modo che l'accesso alla lista dai thread garantisca mutua-esclusione e visibilità delle modifiche.

    E la cosa più semplice è usare l'oggetto LinkedList stesso come "lock".

    HalJordan ha scritto:


    cosa sbaglio da questo ragionamento?
    Il problema è che il synchronized (list) { ... } l'hai messo "a monte" di tutto il thread (per entrambi). Quindi c'è una grossa mutua-esclusione, ovvero prima esegue tutto un thread e poi esegue tutto l'altro thread!! (l'ordine non è prevedibile/garantito, dipende da chi arriva prima ad acquisire il lock).

    P.S. quei Thread.currentThread().interrupt(); non servono a nulla. Un thread che interrompe sé stesso è lecito/permesso ma questo setta solo il flag di interrupted ... non causa altro.
  • Re: Ripetizione di Task

    Non so perchè ho messo quei cafonissimi Thread.currentThread().interrupt();
    Chiaramente hai ragione, a me serve solo che Thread 1 abbia il lock di list, Thread 2 può stampare quando gli pare.(synchronized(list){} solo in t1) Il mio problema è che non riesco a capire perchè quando Thread 1 si ferma, Thread 2 continua a stampare e come se non vedesse quel while

    EDIT: si ferma dopo 20 cicli. é una questione di scheduler?
  • Re: Ripetizione di Task

    HalJordan ha scritto:


    Thread 2 può stampare quando gli pare.(synchronized(list){} solo in t1)
    No, TUTTI i due thread devono acquisire il lock. Anche se in t2 devi "solo" fare un for-each sulla lista, ANCHE qui devi acquisire il lock!
  • Re: Ripetizione di Task

    public static void main(String[] args){
            List<Integer> list=new LinkedList<>();
    
            Thread t1=new Thread(()->{
                   int i;
                   while((i=new Random().nextInt(200))%10!=0){
                       try {
                           Thread.currentThread().sleep(1000);
                       } catch (InterruptedException e) {
                           return;
                       }
                       synchronized(list) {
                           list.add(i);
                           System.out.println("" + i);
                       }
                   }
                   System.out.println("FINALE:"+i);
            });
    
            Thread t2=new Thread(()->{
                while(!t1.isInterrupted()) {
                    try {
                        Thread.currentThread().sleep(10000);
                    } catch (InterruptedException e) {
                        return;
                    }
                    synchronized (list) {
                        int sum = 0;
                        for (Integer i : list) {
                            sum = i + sum;
                        }
                        System.out.println("SOMMA: " + sum);
                    }
                }
            });
            t1.start();
            t2.start();
        }
    non capisco però perchè isInterrupted torna sempre false
  • Re: Ripetizione di Task

    HalJordan ha scritto:


    non capisco però perchè isInterrupted torna sempre false
    Perché da nessuna parte vai a "interrompere" t2.
    Se vuoi, ad esempio, che alla fine di t1 venga "interrotto" t2 (in modo che esca da quel while perenne), si può fare. Ma devi farlo ... finora non l'hai fatto.

    P.S. c'è una differenza "fine" tra
    - il interrupted() static
    e
    - il isInterrupted() non-static (di "istanza")

    Generalmente si usa il primo.
  • Re: Ripetizione di Task

    Ah, e giusto per completezza: il new Random() dovresti farlo una volta sola, non ad ogni estrazione.
  • Re: Ripetizione di Task

    Avevo pensato ad un oggetto di tipo AtomicBoolean come flag, però poi potrei ritrovarmi nel caso in cui t1 smette, t2 stampa ed esce, e non è un caso accettabile.

    Il problema non conosco bene la differenza tra interrupted e isInterrupted, o meglio ho difficoltà nel capirla. Ci provo

    interrupted() riporta lo stato di interruzione del Thread che lo chiama quindi t1.interrupted ritorna lo stato di t1, ma non capisco perchè cancella lo stato o meglio la flag e perchè la seconda chiamata set a false.

    isInterrupted() riporta lo stato di interruzione del Thread su cui viene chiamato. Quindi se faccio t1.isInterrupted in t2 mi dici false perchè t2 non è interrotto.
Devi accedere o registrarti per scrivere nel forum
18 risposte