HOWTO · Java

How to Make a Countdown Timer in Java

This article shows you methods of how to make a countdown timer in Java.

On this page

A Java countdown timer displays the remaining time at regular intervals and stops when it reaches completion. You can build one with ScheduledExecutorService, Timer and TimerTask, or a simple loop with Thread.sleep(). These examples use only Java’s standard library and can be adapted for console output or a user interface.

How a Java Countdown Timer Works

Choose the time source and execution model to fit the application. A scheduled task runs periodically without holding up the thread that started it. A loop with Thread.sleep() is easier to follow, but blocks its current thread between updates. Neither scheduling API guarantees that a callback runs at an exact wall-clock instant: operating-system scheduling and other work can make an update late.

For a display that must stay aligned with real time, calculate the remaining seconds from a deadline on each update instead of assuming every callback arrived exactly one second apart. The examples below use short delays to keep the code readable; adjust the initial value and period for your application.

Create a Console Countdown with ScheduledExecutorService

ScheduledExecutorService is a good default for recurring background work. This complete console program starts at three, prints each value through one, then prints a completion message and shuts down its executor. The task starts immediately and runs once per second.

Run this example:

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);
    }
}

The mutable counter is accessed only by the single scheduled task, so an AtomicInteger is unnecessary here. The completion check runs after the last displayed value, which avoids printing a negative number. If the task can be accessed concurrently, use a thread-safe state design. Call shutdown() when no more work is needed; for an externally controlled stop, retain the ScheduledFuture<?> returned by the scheduling call and cancel it.

Display a Countdown with Timer and TimerTask

Timer schedules a TimerTask after an initial delay and at a fixed period. The following console example uses the same completion sequence. Its timer is canceled when the countdown finishes so its thread does not keep the application alive.

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);
    }
}

For a Swing label, do not call setText directly from TimerTask.run(): the task runs on the timer’s background thread. Post the UI update to Swing’s event-dispatch thread with SwingUtilities.invokeLater(() -> label.setText(...)). Stop the timer when the window closes or the countdown is canceled. Timer uses one background thread, so a slow task can delay other tasks scheduled on that same timer.

Create a Blocking Countdown with Thread.sleep()

A loop with Thread.sleep() is appropriate for a small command-line task when blocking the current thread is acceptable. sleep pauses the current thread for approximately the requested duration; it does not provide exact timing.

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!");
    }
}

When interruption occurs, the code restores the thread’s interrupted status and exits instead of swallowing the cancellation signal. Do not run this loop on a Swing event-dispatch thread: the sleep prevents that thread from processing input and repaint events. Use a scheduler or a Swing timer for a responsive interface.

Choose a Timer and Handle Completion Safely

Use ScheduledExecutorService when you need background scheduling, explicit cancellation, or a managed executor lifecycle. Timer can suit a small single-threaded recurring task, but its single worker means one slow task delays the rest. Use Thread.sleep() for a simple sequential delay where blocking is intended. For Swing, schedule display updates on the event-dispatch thread, or use Swing’s own timer for UI-oriented callbacks.

Define the completion behavior before scheduling: decide which values to display, what action happens at zero, and how cancellation works. Shut down an executor or cancel a timer when the countdown completes or its owner is closed. If the task is interrupted, preserve that signal or stop cleanly. For accurate elapsed-time displays, derive the remaining value from a deadline because periodic callbacks may drift or arrive late.