Mastering Java Scheduled Tasks: Timer vs ScheduledExecutorService
This article compares Java's Timer and ScheduledExecutorService for scheduling tasks, explains their internal workings, demonstrates common pitfalls, and provides multiple code examples showing how to schedule one‑off, fixed‑rate, and fixed‑delay tasks using both Runnable and Callable.
Timer
The Timer class can schedule tasks but offers few methods, lacks flexibility, and performs poorly under high concurrency. When a Timer is created, it starts a dedicated thread that repeatedly pulls tasks from a queue and executes them.
Example of two periodic tasks that print the current thread name every 2 seconds:
public class TimerDemo {
public static void main(String[] args) {
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}, new Date(), 2000);
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ", task...");
}
}, new Date(), 2000);
}
}Both tasks run on the same thread, as shown in the console output. The Timer class inherits from Thread, sets the thread name on creation, and runs its run method, which contains a loop that continuously fetches tasks from the queue.
If an exception occurs inside a scheduled task, the entire Timer stops, which is undesirable. Additionally, Timer schedules based on absolute time, making it sensitive to system clock changes.
ScheduledExecutorService
The ScheduledExecutorService interface combines task scheduling with thread‑pool capabilities. Its primary implementation is ScheduledThreadPoolExecutor, which extends ThreadPoolExecutor. This design allows flexible, high‑performance scheduling of both one‑off and periodic tasks.
Scheduling a delayed task using Callable:
public class ScheduledExecutorServiceDemo {
public static void main(String[] args) throws Exception {
ScheduledExecutorService sch = new ScheduledThreadPoolExecutor(5);
ScheduledFuture<String> f1 = sch.schedule(() -> {
System.out.println(Thread.currentThread().getName() + " 当前时间: " + System.currentTimeMillis());
return "Task1";
}, 5, TimeUnit.SECONDS);
ScheduledFuture<String> f2 = sch.schedule(() -> {
System.out.println(Thread.currentThread().getName() + " 当前时间: " + System.currentTimeMillis());
return "Task2";
}, 6, TimeUnit.SECONDS);
System.out.println(f1.get());
System.out.println(f2.get());
}
}The two tasks start at nearly the same moment; the second begins one second later because its delay is 6 seconds versus 5 seconds for the first.
Scheduling a delayed task using Runnable:
public class ScheduledExecutorServiceDemo2 {
public static void main(String[] args) throws Exception {
ScheduledExecutorService sch = new ScheduledThreadPoolExecutor(5);
sch.schedule(() -> {
System.out.println("A起始时间: " + Thread.currentThread().getName() + " 当前时间: " + System.currentTimeMillis());
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("A结束时间: " + Thread.currentThread().getName() + " 当前时间: " + System.currentTimeMillis());
}, 2, TimeUnit.SECONDS);
sch.schedule(() -> {
System.out.println("B起始时间: " + Thread.currentThread().getName() + " 当前时间: " + System.currentTimeMillis());
try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("B结束时间: " + Thread.currentThread().getName() + " 当前时间: " + System.currentTimeMillis());
}, 2, TimeUnit.SECONDS);
}
}When a task’s execution time exceeds its period, scheduleAtFixedRate will start the next execution immediately after the current one finishes, potentially causing overlapping runs.
Example using scheduleAtFixedRate where the task runs longer than the period:
public class ScheduledExecutorServiceDemo3 {
public static void main(String[] args) throws Exception {
ScheduledExecutorService sch = new ScheduledThreadPoolExecutor(5);
sch.scheduleAtFixedRate(() -> {
System.out.println("A起始时间: " + Thread.currentThread().getName() + " 当前时间: " + System.currentTimeMillis());
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("A结束时间: " + Thread.currentThread().getName() + " 当前时间: " + System.currentTimeMillis());
}, 1, 2, TimeUnit.SECONDS);
}
}Using scheduleWithFixedDelay ensures a fixed pause between the end of one execution and the start of the next:
public class ScheduledExecutorServiceDemo4 {
public static void main(String[] args) throws Exception {
ScheduledExecutorService sch = new ScheduledThreadPoolExecutor(5);
sch.scheduleWithFixedDelay(() -> {
System.out.println("A起始时间: " + Thread.currentThread().getName() + " 当前时间: " + System.currentTimeMillis());
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("A结束时间: " + Thread.currentThread().getName() + " 当前时间: " + System.currentTimeMillis());
}, 1, 2, TimeUnit.SECONDS);
}
}The second parameter of scheduleWithFixedDelay specifies the initial delay, and the third parameter defines the fixed delay between successive executions.
In summary, ScheduledExecutorService offers a more robust, flexible, and thread‑safe way to schedule tasks compared to Timer, especially in high‑concurrency environments.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
