HOWTO · Java

Crea un conto alla rovescia in Java

Questo articolo mostra i metodi per creare un conto alla rovescia in Java.

In questa pagina

Un timer per il conto alla rovescia in Java mostra il tempo rimanente a intervalli regolari e si arresta al termine. Puoi realizzarlo con ScheduledExecutorService, Timer e TimerTask, oppure con un semplice ciclo che usa Thread.sleep(). Gli esempi usano solo la libreria standard di Java e si possono adattare all’output su console o a un’interfaccia utente.

Come funziona un timer per il conto alla rovescia in Java

Scegli la sorgente temporale e il modello di esecuzione più adatti all’applicazione. Un’attività pianificata viene eseguita periodicamente senza bloccare il thread che l’ha avviata. Un ciclo con Thread.sleep() è più semplice da seguire, ma blocca il thread corrente tra un aggiornamento e l’altro. Nessuna delle due API di pianificazione garantisce che una callback venga eseguita in un istante esatto dell’orologio: la pianificazione del sistema operativo e altre attività possono ritardare un aggiornamento.

Se il display deve restare sincronizzato con l’ora effettiva, a ogni aggiornamento calcola i secondi rimanenti a partire da una scadenza, invece di presumere che ogni callback sia arrivata esattamente a un secondo di distanza. Gli esempi usano intervalli brevi per mantenere il codice leggibile; adatta il valore iniziale e il periodo alle esigenze dell’applicazione.

Creare un conto alla rovescia su console con ScheduledExecutorService

ScheduledExecutorService è una buona scelta predefinita per le attività ricorrenti in background. Questo programma completo per console parte da tre, stampa ogni valore fino a uno, poi mostra un messaggio di completamento e arresta l’executor. L’attività inizia subito e viene eseguita una volta al secondo.

Esegui questo esempio:

Il codice e le etichette o gli output visibili in questa sezione restano in inglese.

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Countdown {
    public static void main(String[] args) {
        ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
        final int[] remaining = {3};

        scheduler.scheduleAtFixedRate(() -> {
            if (remaining[0] > 0) {
                System.out.println(remaining[0]--);
            } else {
                System.out.println("Countdown complete!");
                scheduler.shutdown();
            }
        }, 0, 1, TimeUnit.SECONDS);
    }
}

Il contatore modificabile è usato solo dall’unica attività pianificata, quindi qui non serve un AtomicInteger. Il controllo del completamento avviene dopo la visualizzazione dell’ultimo valore, evitando così di stampare un numero negativo. Se più thread possono accedere all’attività, usa una gestione dello stato thread-safe. Quando non serve più altro lavoro, chiama shutdown(); per interrompere l’attività dall’esterno, conserva lo ScheduledFuture<?> restituito dalla chiamata di pianificazione e annullalo.

Visualizzare un conto alla rovescia con Timer e TimerTask

Timer pianifica un TimerTask dopo un ritardo iniziale e a intervalli fissi. Il seguente esempio per console usa la stessa sequenza di completamento. Al termine del conto alla rovescia, il timer viene annullato affinché il suo thread non mantenga in esecuzione l’applicazione.

Il codice e le etichette o gli output visibili in questa sezione restano in inglese.

import java.util.Timer;
import java.util.TimerTask;

public class TimerCountdown {
    public static void main(String[] args) {
        Timer timer = new Timer();
        final int[] remaining = {3};

        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                if (remaining[0] > 0) {
                    System.out.println(remaining[0]--);
                } else {
                    System.out.println("Countdown complete!");
                    timer.cancel();
                }
            }
        }, 0, 1000);
    }
}

Per aggiornare un’etichetta Swing, non chiamare direttamente setText da TimerTask.run(): l’attività viene eseguita sul thread in background del timer. Invia l’aggiornamento dell’interfaccia al thread di gestione degli eventi di Swing con SwingUtilities.invokeLater(() -> label.setText(...)). Arresta il timer quando si chiude la finestra o si annulla il conto alla rovescia. Timer usa un solo thread in background, quindi un’attività lenta può ritardare le altre attività pianificate sullo stesso timer.

Creare un conto alla rovescia bloccante con Thread.sleep()

Un ciclo con Thread.sleep() è adatto a una semplice attività da riga di comando quando è accettabile bloccare il thread corrente. sleep sospende il thread corrente per una durata approssimativamente pari a quella richiesta; non garantisce una temporizzazione esatta.

Il codice e le etichette o gli output visibili in questa sezione restano in inglese.

public class BlockingCountdown {
    public static void main(String[] args) {
        for (int remaining = 3; remaining > 0; remaining--) {
            System.out.println(remaining);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                System.out.println("Countdown interrupted.");
                return;
            }
        }
        System.out.println("Countdown complete!");
    }
}

Quando si verifica un’interruzione, il codice ripristina lo stato di interruzione del thread ed esce, invece di ignorare il segnale di annullamento. Non eseguire questo ciclo sul thread di gestione degli eventi di Swing: la sospensione impedisce a quel thread di elaborare l’input e gli eventi di ridisegno. Per un’interfaccia reattiva, usa uno scheduler o un timer Swing.

Scegliere un timer e gestire il completamento in sicurezza

Usa ScheduledExecutorService quando servono pianificazione in background, annullamento esplicito o un ciclo di vita dell’executor gestito. Timer può essere adatto a una piccola attività ricorrente a thread singolo, ma il suo unico thread di lavoro fa sì che un’attività lenta ritardi le altre. Usa Thread.sleep() per una semplice attesa sequenziale quando il blocco è intenzionale. Con Swing, pianifica gli aggiornamenti del display sul thread di gestione degli eventi oppure usa il timer integrato di Swing per le callback orientate all’interfaccia.

Definisci il comportamento al completamento prima di pianificare l’attività: decidi quali valori mostrare, cosa fare quando si arriva a zero e come gestire l’annullamento. Arresta l’executor o annulla il timer quando il conto alla rovescia termina o il componente proprietario viene chiuso. Se l’attività viene interrotta, conserva il segnale oppure termina in modo pulito. Per visualizzare il tempo trascorso con precisione, calcola il valore rimanente a partire da una scadenza, perché le callback periodiche possono accumulare ritardi o arrivare in ritardo.