Java > Concurrency and Multithreading > Executors and Thread Pools > Scheduled Executors
ScheduledExecutorService Example: Periodic Task Execution
This example demonstrates how to use `ScheduledExecutorService` to schedule a task to run periodically. It showcases both `scheduleAtFixedRate` and `scheduleWithFixedDelay` methods for periodic execution, highlighting their differences.
Code Snippet
This code snippet demonstrates the difference between `scheduleAtFixedRate` and `scheduleWithFixedDelay`. `fixedRateTask` is scheduled to run every 3 seconds, starting after an initial delay of 1 second. `fixedDelayTask` is scheduled to run with a 3-second delay between the end of one execution and the start of the next, also starting after an initial delay of 1 second. The `Thread.sleep()` calls simulate some work being done by the tasks, allowing you to observe the timing differences. The scheduler's pool size is set to 2 because both tasks will likely run concurrently. The main thread sleeps for 15 seconds to allow tasks to execute before shutting down the executor.
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class PeriodicScheduledExecutorExample {
public static void main(String[] args) throws InterruptedException {
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2); // Increased pool size to accommodate both tasks
Runnable fixedRateTask = () -> {
try {
System.out.println("Fixed Rate Task - Start: " + System.currentTimeMillis() / 1000);
Thread.sleep(2000); // Simulate some work
System.out.println("Fixed Rate Task - End: " + System.currentTimeMillis() / 1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("Fixed Rate Task interrupted: " + e.getMessage());
}
};
Runnable fixedDelayTask = () -> {
try {
System.out.println("Fixed Delay Task - Start: " + System.currentTimeMillis() / 1000);
Thread.sleep(3000); // Simulate some work
System.out.println("Fixed Delay Task - End: " + System.currentTimeMillis() / 1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("Fixed Delay Task interrupted: " + e.getMessage());
}
};
System.out.println("Scheduling fixed rate task (initial delay 1 second, period 3 seconds)...");
scheduler.scheduleAtFixedRate(fixedRateTask, 1, 3, TimeUnit.SECONDS);
System.out.println("Scheduling fixed delay task (initial delay 1 second, delay 3 seconds)...");
scheduler.scheduleWithFixedDelay(fixedDelayTask, 1, 3, TimeUnit.SECONDS);
Thread.sleep(15000); // Let the tasks run for 15 seconds
scheduler.shutdown();
try {
scheduler.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
System.err.println("Interrupted while waiting for termination: " + e.getMessage());
}
}
}
scheduleAtFixedRate vs. scheduleWithFixedDelay
The key difference lies in how the period is interpreted. scheduleAtFixedRate
guarantees that the task starts every `period` seconds, regardless of how long the previous execution took. If a task takes longer than the period, subsequent executions may overlap. scheduleWithFixedDelay
, on the other hand, ensures that there's a `delay` of seconds between the *end* of one execution and the *start* of the next.
Exception Handling in Periodic Tasks
Proper exception handling is crucial in periodic tasks. If an exception is thrown and not caught within a task scheduled with scheduleAtFixedRate
or scheduleWithFixedDelay
, the task will be silently cancelled. This can lead to unexpected behavior and make debugging difficult. Always wrap the task's code in a try-catch
block to handle exceptions gracefully and prevent them from terminating the task.
Real-Life Use Case
Periodic tasks are essential for many applications. Examples include:
Best Practices
ScheduledExecutorService
gracefully to avoid resource leaks. Use shutdown()
followed by awaitTermination()
.scheduleAtFixedRate
, be aware that tasks can overlap if they take longer than the period. Consider using scheduleWithFixedDelay
if you want to ensure that tasks don't run concurrently.
Interview Tip
Be prepared to explain the trade-offs between using scheduleAtFixedRate
and scheduleWithFixedDelay
. Understand how each method handles the execution time of the task and the implications for scheduling accuracy. Also, be able to discuss the importance of exception handling in periodic tasks.
When to use them
Use `scheduleAtFixedRate` for tasks where you want to ensure a consistent execution rate, even if a previous execution takes longer than the period. Use `scheduleWithFixedDelay` for tasks where you want to ensure a minimum delay between executions, regardless of how long the task takes to complete.
Memory Footprint
The memory footprint depends on the pool size and the number of scheduled tasks. Each thread in the pool consumes memory. Also the tasks themselves consume memory, especially if they maintain state or large data structures. Monitor memory usage to avoid potential issues.
Alternatives
Timer
class is an alternative, but it's generally less preferred due to its single-threaded nature and less robust exception handling.
Pros
Cons
scheduleAtFixedRate
can lead to overlapping executions if tasks take longer than the period.
FAQ
-
What happens if a task throws an exception when using `scheduleAtFixedRate`?
The task is silently cancelled, and no further executions will occur. Always wrap the task's code in a `try-catch` block to handle exceptions. -
How can I cancel a scheduled task?
The `scheduleAtFixedRate` and `scheduleWithFixedDelay` methods return a `ScheduledFuture` object, which can be used to cancel the task using its `cancel()` method. -
Is it possible to change the period of a scheduled task after it has been scheduled?
No, the period of a scheduled task cannot be changed after it has been scheduled. You would need to cancel the existing task and schedule a new task with the desired period.