1. What is a ScheduledThreadPoolExecutor?
ScheduledThreadPoolExecutor is a part of Java’s java.util.concurrent package. It allows you to schedule tasks to run after a delay or periodically, using a pool of threads.
It is a powerful replacement for the older Timer and TimerTask classes, offering:
- Better performance
- More robust error handling
- Flexible thread pool management
- Fine-grained control over scheduling
2. Where is it defined?
java.util.concurrent.ScheduledThreadPoolExecutor
It implements:
ScheduledExecutorServiceExecutorService
2. Why use ScheduledThreadPoolExecutor instead of Timer?
| Feature | ScheduledThreadPoolExecutor | Timer |
|---|---|---|
| Uses thread pool | Yes | No (single thread only) |
| Handles multiple tasks | Concurrently | Sequentially |
| Exception handling | Better — doesn’t halt on exception | One exception can stop the timer |
| Scheduling | Fixed delay or fixed rate | Only fixed delay |
| Thread management | Scalable and configurable | Rigid |
3. How to Create a ScheduledThreadPoolExecutor
Option 1: Direct constructor
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(4);
4 means it can run 4 scheduled tasks concurrently.
Option 2: Using Executors factory
ScheduledExecutorService executor = Executors.newScheduledThreadPool(4);
Returns a ScheduledExecutorService, which is the interface.
4. Scheduling Methods
The following are the most commonly used methods for scheduling tasks:
A. schedule(Runnable command, long delay, TimeUnit unit)
Schedules a one-time task after a delay.
executor.schedule(() -> System.out.println("Task runs after 3 seconds"), 3, TimeUnit.SECONDS);
B. scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
- Schedules a task to run repeatedly:
- Starts after
initialDelay - Then runs every
periodregardless of how long the task takes
- Starts after
- If a task runs longer than the period, tasks may overlap.
executor.scheduleAtFixedRate(() -> System.out.println("Fixed Rate Task"), 1, 2, TimeUnit.SECONDS);
C. scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
Schedules a task to run repeatedly:
- Starts after
initialDelay - Each execution is delayed by
delayafter the previous run completes
Ensures no overlap.
executor.scheduleWithFixedDelay(() -> System.out.println("Fixed Delay Task"), 1, 2, TimeUnit.SECONDS);
5. Example: One-Time and Repeating Tasks
import java.util.concurrent.*;
public class ScheduledExecutorExample {
public static void main(String[] args) {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
// One-time task
executor.schedule(() -> System.out.println("Run once after 3 seconds"), 3, TimeUnit.SECONDS);
// Fixed-rate task
executor.scheduleAtFixedRate(() -> {
System.out.println("Fixed rate: " + System.currentTimeMillis());
}, 1, 5, TimeUnit.SECONDS);
// Fixed-delay task
executor.scheduleWithFixedDelay(() -> {
System.out.println("Fixed delay: " + System.currentTimeMillis());
}, 1, 5, TimeUnit.SECONDS);
}
}
6. Shutting Down the Executor
It’s important to shut down the executor to release resources:
executor.shutdown(); // Waits for tasks to finish executor.shutdownNow(); // Attempts to stop all tasks immediately
You can also use awaitTermination() to wait:
executor.awaitTermination(10, TimeUnit.SECONDS);
7. Handling Exceptions in Tasks
If a task throws an unchecked exception, it will be suppressed silently. You must handle exceptions inside the task:
executor.schedule(() -> {
try {
// risky logic
} catch (Exception e) {
e.printStackTrace();
}
}, 1, TimeUnit.SECONDS);
8. Use Cases
- Periodic status polling (e.g., database, sensors)
- Repeating background jobs (e.g., email reminders)
- Time-based cache eviction
- System health monitoring
- One-time delayed actions (e.g., logout timeout)
9. Best Practices
- Always shut down the executor when done to avoid thread leaks.
- Prefer
scheduleWithFixedDelaywhen task duration is variable. - Use try-catch inside tasks to avoid silent failure.
- Choose an appropriate thread pool size based on concurrency needs.
- Avoid blocking tasks inside the executor (e.g., I/O or sleep), or the thread pool may exhaust.
10. Advanced: Customize ThreadPoolExecutor
You can extend ScheduledThreadPoolExecutor for custom behavior:
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(2) {
@Override
protected void beforeExecute(Thread t, Runnable r) {
System.out.println("About to run task: " + r);
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
System.out.println("Finished task: " + r);
}
};
