Le classi Timer e TimerTask per la Pianificazione di Attività in Java

Concetti Chiave
  • Le classi Timer e TimerTask sono utilizzate per pianificare attività da eseguire in momenti specifici o a intervalli regolari.
  • TimerTask estende l'interfaccia Runnable e contiene il codice da eseguire quando il task è attivato.
  • Timer gestisce la pianificazione e l'esecuzione dei task, permettendo di specificare ritardi e frequenze di esecuzione.
  • I task possono essere programmati per l'esecuzione una tantum o a intervalli regolari, e possono essere cancellati quando non sono più necessari.
  • I task demone terminano automaticamente quando il programma principale termina, mentre i task normali richiedono una chiamata esplicita a cancel() per essere terminati.
  • È possibile utilizzare schedule() e scheduleAtFixedRate() per pianificare l'esecuzione dei task in base a ritardi specifici o a intervalli regolari.

Timer e TimerTask

Una caratteristica interessante e utile offerta dalla libreria standard java.util è la capacità di pianificare un'attività per l'esecuzione in un momento futuro.

Le classi che supportano questa funzionalità sono Timer e TimerTask. Utilizzando queste classi, possiamo creare un thread che funziona in background, aspettando un momento specifico. Quando arriva il momento, l'attività collegata a quel thread viene eseguita. Varie opzioni permettono di pianificare un'attività per l'esecuzione ripetuta, e di pianificare un'attività per essere eseguita in una data specifica. Anche se è sempre stato possibile creare manualmente un'attività che sarebbe stata eseguita in un momento specifico utilizzando la classe Thread, Timer e TimerTask semplificano notevolmente questo processo.

Timer e TimerTask funzionano insieme. Timer è la classe che si utilizza per pianificare un'attività per l'esecuzione. L'attività che viene pianificata deve essere un'istanza di TimerTask. Quindi, per pianificare un'attività, si crea dapprima un oggetto TimerTask e poi lo si pianifica per l'esecuzione utilizzando un'istanza di Timer.

TimerTask implementa l'interfaccia Runnable; quindi, può essere utilizzata per creare un thread di esecuzione. Il suo costruttore è mostrato qui:

protected TimerTask()

TimerTask definisce i metodi mostrati nella tabella che segue. Si noti che run() è abstract, il che significa che deve essere sovrascritto. Il metodo run(), definito dall'interfaccia Runnable, contiene il codice che verrà eseguito. Pertanto, il modo più semplice per creare un timer task è estendere TimerTask e sovrascrivere run().

Metodo Descrizione
boolean cancel() Termina l'attività. Restituisce true se un'esecuzione dell'attività è stata annullata. Altrimenti, restituisce false.
abstract void run() Contiene il codice per l'attività del timer.
long scheduledExecutionTime() Restituisce il momento in cui l'ultima esecuzione dell'attività era pianificata per essere avvenuta.
Tabella 1: Metodi della classe TimerTask

Una volta che un task è stato creato, viene programmato per l'esecuzione da un oggetto di tipo Timer. I costruttori per Timer sono mostrati qui:

Timer()
Timer(boolean threadDemone)
Timer(String nomeThread)
Timer(String nomeThread, boolean threadDemone)

La prima versione crea un oggetto Timer che funziona come un thread normale. La seconda usa un thread demone se threadDemone è true. Un thread demone verrà eseguito solo finché il resto del programma continua ad eseguire. Il terzo e quarto costruttore permettono di specificare un nome per il thread Timer. I metodi definiti da Timer sono mostrati nella tabella seguente:

Metodo Descrizione
void cancel() Cancella il thread del timer.
int purge() Elimina i task cancellati dalla coda del timer.
void schedule(TimerTask task, long attesa) task è programmato per l'esecuzione dopo che il periodo passato in attesa è trascorso. Il parametro attesa è specificato in millisecondi.
void schedule(TimerTask task, long attesa, long ripetizione) task è programmato per l'esecuzione dopo che il periodo passato in attesa è trascorso. Il task viene poi eseguito ripetutamente all'intervallo specificato da ripetizione. Sia attesa che ripetizione sono specificati in millisecondi.
void schedule(TimerTask task, Date tempoTarget) task è programmato per l'esecuzione al tempo specificato da tempoTarget.
void schedule(TimerTask task, Date tempoTarget, long ripetizione) task è programmato per l'esecuzione al tempo specificato da tempoTarget. Il task viene poi eseguito ripetutamente all'intervallo passato in ripetizione. Il parametro ripetizione è specificato in millisecondi.
void scheduleAtFixedRate(TimerTask task, long attesa, long ripetizione) task è programmato per l'esecuzione dopo che il periodo passato in attesa è trascorso. Il task viene poi eseguito ripetutamente all'intervallo specificato da ripetizione. Sia attesa che ripetizione sono specificati in millisecondi. Il tempo di ogni ripetizione è relativo alla prima esecuzione, non all'esecuzione precedente. Pertanto, il tasso complessivo di esecuzione è fisso.
void scheduleAtFixedRate(TimerTask task, Date tempoTarget, long ripetizione) task è programmato per l'esecuzione al tempo specificato da tempoTarget. Il task viene poi eseguito ripetutamente all'intervallo passato in ripetizione. Il parametro ripetizione è specificato in millisecondi. Il tempo di ogni ripetizione è relativo alla prima esecuzione, non all'esecuzione precedente. Pertanto, il tasso complessivo di esecuzione è fisso.
Tabella 2: Metodi della classe Timer

Una volta che un Timer è stato creato, bisogna programmare l'avvio di un task chiamando schedule() sull'oggetto Timer che avete creato. Come mostra la tabella di sopra, ci sono diverse forme di schedule() che permettono di programmare task in una varietà di modi.

Se si crea un task non-demone, allora bisogna chiamare cancel() per terminare il task quando il programma termina. Se non si fa questo, allora il programma potrebbe "bloccarsi" per un certo periodo di tempo oppure indefinitamente, a meno che non si chiami cancel(). Se si crea un task demone, allora il programma terminerà automaticamente quando tutti i thread non-demone sono terminati.

Il seguente programma dimostra Timer e TimerTask. Definisce un timer task il cui metodo run() visualizza il messaggio "Timer task eseguito." Questo task è programmato per funzionare una volta ogni mezzo secondo dopo un ritardo iniziale di un secondo.

// Un Esempio di Utilizzo di Timer e TimerTask
import java.util.*;

// Definisce un TimerTask che
// stampa un messaggio quando viene eseguito.
class MioTimerTask extends TimerTask {

    static int numeroVolte = 0;

    public void run() {
        numeroVolte++;
        System.out.println("Timer task eseguito " + numeroVolte + " volte.");
    }

}

class EsempioTimerTask {

    public static void main(String[] args) {
        // Crea un oggetto MioTimerTask e un Timer.
        // Il Timer è un thread che esegue i task programmati.
        MioTimerTask mioTask = new MioTimerTask();
        Timer mioTimer = new Timer();

        // Imposta un ritardo iniziale di 1 secondo,
        // poi ripete ogni mezzo secondo.
        // Sia il ritardo iniziale che
        // l'intervallo di ripetizione sono specificati in millisecondi.
        mioTimer.schedule(mioTask, 1000, 500);

        try {
            // Attende 5 secondi prima di cancellare il timer.
            // Questo permette di vedere il task eseguito più volte.
            // Dopo 5 secondi, il programma termina e il timer viene cancellato.
            Thread.sleep(5000);
        } catch (InterruptedException eccezione) {}

        // Cancella il timer e termina il task.
        mioTimer.cancel();
    }

}

L'output di questo programma è simile al seguente:

Timer task eseguito 1 volte.
Timer task eseguito 2 volte.
Timer task eseguito 3 volte.
Timer task eseguito 4 volte.
Timer task eseguito 5 volte.
Timer task eseguito 6 volte.
Timer task eseguito 7 volte.
Timer task eseguito 8 volte.