Java Timer
Java Programming
Timer Setup
Java Tutorial
Coding Guide

How to set a Timer in Java?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Java offers more than one way to schedule code for later execution. The older java.util.Timer API still works for simple cases, but for most modern applications ScheduledExecutorService is the better choice because it is more flexible, better behaved with exceptions, and fits the rest of the concurrency APIs.

The Old-School Option: java.util.Timer

Timer runs scheduled tasks on a background thread. It is easy to use for simple one-off or repeating jobs:

java
1import java.util.Timer;
2import java.util.TimerTask;
3
4public class TimerExample {
5    public static void main(String[] args) {
6        Timer timer = new Timer();
7
8        TimerTask task = new TimerTask() {
9            @Override
10            public void run() {
11                System.out.println("Timer fired");
12            }
13        };
14
15        timer.schedule(task, 2000);
16    }
17}

This schedules the task once after a two-second delay.

For repeated execution:

java
timer.scheduleAtFixedRate(task, 1000, 3000);

That starts after one second and then repeats every three seconds.

Why ScheduledExecutorService Is Usually Better

The modern alternative is ScheduledExecutorService from java.util.concurrent. It integrates more naturally with thread pools and is usually the better default for production code.

Here is a one-time scheduled task:

java
1import java.util.concurrent.Executors;
2import java.util.concurrent.ScheduledExecutorService;
3import java.util.concurrent.TimeUnit;
4
5public class ScheduledOnceExample {
6    public static void main(String[] args) {
7        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
8
9        scheduler.schedule(() -> {
10            System.out.println("Executed once after 2 seconds");
11            scheduler.shutdown();
12        }, 2, TimeUnit.SECONDS);
13    }
14}

And here is a repeating task:

java
1import java.util.concurrent.Executors;
2import java.util.concurrent.ScheduledExecutorService;
3import java.util.concurrent.TimeUnit;
4
5public class ScheduledRepeatExample {
6    public static void main(String[] args) {
7        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
8
9        scheduler.scheduleAtFixedRate(() -> {
10            System.out.println("Tick: " + System.currentTimeMillis());
11        }, 1, 2, TimeUnit.SECONDS);
12
13        scheduler.schedule(() -> {
14            scheduler.shutdown();
15            System.out.println("Scheduler stopped");
16        }, 10, TimeUnit.SECONDS);
17    }
18}

This gives you predictable scheduling with a clean shutdown path.

Fixed Rate vs Fixed Delay

Java exposes two repeating patterns with ScheduledExecutorService:

  • 'scheduleAtFixedRate tries to maintain a regular schedule'
  • 'scheduleWithFixedDelay waits a fixed delay after one execution finishes before starting the next'

Example of fixed delay:

java
scheduler.scheduleWithFixedDelay(() -> {
    System.out.println("Run, then wait 2 seconds");
}, 1, 2, TimeUnit.SECONDS);

Use fixed rate when the schedule itself matters, such as polling every minute. Use fixed delay when each run may take variable time and you want breathing room between runs.

Cancelling a Timer Task

You often need the ability to stop the scheduled work. With ScheduledExecutorService, keep the returned handle:

java
1import java.util.concurrent.Executors;
2import java.util.concurrent.ScheduledExecutorService;
3import java.util.concurrent.ScheduledFuture;
4import java.util.concurrent.TimeUnit;
5
6public class CancelExample {
7    public static void main(String[] args) {
8        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
9
10        ScheduledFuture<?> future = scheduler.scheduleAtFixedRate(() -> {
11            System.out.println("Running...");
12        }, 0, 1, TimeUnit.SECONDS);
13
14        scheduler.schedule(() -> {
15            future.cancel(false);
16            scheduler.shutdown();
17        }, 5, TimeUnit.SECONDS);
18    }
19}

This is one of the reasons the executor-based API is more practical than the older Timer class.

When Timer Is Still Acceptable

Timer is fine for small programs, throwaway tools, or examples where only one simple background schedule exists. Once you care about multiple tasks, resilience, or integration with other concurrency features, the executor API is usually worth it.

Common Pitfalls

A common mistake is forgetting to shut down the scheduler. Background threads can keep the application alive longer than expected.

Another pitfall is using Timer for tasks that may block or throw exceptions. Because Timer is relatively simple and often single-threaded, one bad task can interfere with the rest of the schedule.

Developers also confuse fixed rate and fixed delay. They sound similar, but they produce different timing behavior under load.

Finally, scheduled tasks should stay small and quick where possible. A timer is not a substitute for a full job system.

Summary

  • Java can schedule tasks with either Timer or ScheduledExecutorService.
  • 'ScheduledExecutorService is usually the better modern default.'
  • Use schedule() for one-time work and fixed-rate or fixed-delay methods for repetition.
  • Keep a handle if you need to cancel a repeating task.
  • Shut schedulers down cleanly and choose the timing mode that matches your workload.

Course illustration
Course illustration

All Rights Reserved.