Научитесь выполнять задачу по истечении определенного периода времени или выполнять ее периодически с помощью класса ScheduledExecutorService в Java с использованием ScheduledThreadPoolExecutor.
1. Интерфейс ScheduledExecutorService
По умолчанию фреймворк Executor предоставляет класс ThreadPoolExecutor для выполнения задач Callable и Runnable с пулом потоков, что помогает нам избежать всего шаблонного кода создания потоков. Когда мы отправляем задачу исполнителю, она выполняется как можно скорее, в соответствии с конфигурацией исполнителя.
Но когда мы не заинтересованы в скорейшем выполнении задачи и хотим выполнять ее через определенный промежуток времени или делать это периодически, мы можем использовать интерфейс ScheduledExecutorService вместе с его реализацией, а именно классом ScheduledThreadPoolExecutor.
открытый интерфейс ScheduledExecutorService расширяет ExecutorService{public ScheduledFuture<?> расписание(команда Runnable,длительная задержка, единица измерения TimeUnit);public <V> ScheduledFuture<V> schedule(Callable<V> отзывной,длительная задержка, единица измерения TimeUnit);public ScheduledFuture<?> scheduleAtFixedRate(команда Runnable,длинная начальная задержка,длительный период,единица измерения TimeUnit);public ScheduledFuture<?> scheduleWithFixedDelay(команда Runnable,длинная начальная задержка,длительная задержка,единица измерения TimeUnit);}
- Методы schedule() создают задачи с различными задержками и возвращают объект задачи, который можно использовать для отмены или проверки выполнения.
- Методы scheduleAtFixedRate() и scheduleWithFixedDelay() создают и выполняют задачи, которые запускаются периодически до тех пор, пока не будут отменены.
- Нулевые и отрицательные задержки(но не периоды) также допускаются в методах расписания и рассматриваются как запросы на немедленное выполнение.
- Все методы планирования принимают в качестве аргументов относительные задержки и периоды, а не абсолютное время или даты.
- Чтобы запланировать задачу на определенную дату в будущем, мы можем использовать API следующим образом: schedule(task, date.getTime() — System.currentTimeMillis(), TimeUnit.MILLISECONDS).
- Все методы возвращают объект ScheduledFuture, представляющий собой отложенное действие с результатом, которое можно отменить.
2. Примеры ScheduledExecutorService
В этом примере мы выполним простую задачу в различных сценариях, чтобы лучше понять использование этого интерфейса.
class Task implements Callable<String> {private final String name;public Task(String name) {this.name = name;}@Overridepublic String call() throws Exception {return "Task [" + name + "] executed on : " + LocalDateTime.now().toString();}}
2.1. Выполнение одиночного исполнения после задержки
В следующем примере мы выполняем задачу через 10 секунд. Задача будет выполнена только один раз. Исполнитель будет остановлен, чтобы не принимать больше задач. Исполнитель будет ждать выполнения задачи максимум 1 час. Если задача не будет выполнена в течение 1 часа, она будет принудительно завершена.
public class ScheduledExecutorServiceExample {public static void main(String[] args) throws InterruptedException, ExecutionException {ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);System.out.println("Task scheduled to execute after 10 seconds at : " + LocalDateTime.now().toString());Task task = new Task("App-Task");ScheduledFuture<?> result = executor.schedule(task, 10, TimeUnit.SECONDS);System.out.println("Shutdown and await requested at : " + LocalDateTime.now().toString());shutdownAndAwaitTermination(executor);}static void shutdownAndAwaitTermination(ExecutorService executorService) {executorService.shutdown();try {if(!executorService.awaitTermination(1, TimeUnit.HOURS)) {executorService.shutdownNow();}} catch(InterruptedException ie) {executorService.shutdownNow();Thread.currentThread().interrupt();}}}
Вывод программы.
Task scheduled to execute after 10 seconds at : 2022-08-07T15:29:28.347677800Shutdown and await requested at : 2022-08-07T15:29:28.347677800Task [App-Task] executed on : 2022-08-07T15:29:38.365194500
2.2. Периодически выполняйте задачу
Если мы хотим выполнить задачу в определенное время, мы можем использовать методы scheduleWithFixedDelay() или scheduleWithFixedRate(). Они продолжат выполнение задач до тех пор, пока не будут отменены или прерваны.
При использовании метода scheduleWithFixedDelay() гарантированно существует разница во времени между двумя выполнениями задачи. Второе выполнение начнется только после заданного времени задержки с момента завершения первого выполнения.
Таким образом, в следующем примере, если выполнение задачи занимает 5 секунд и первое выполнение началось в 1:00:00 ночи, то второе выполнение начнется в 1:00:15 ночи.
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);Task task = new Task("App-Task");ScheduledFuture<?> result = executor1.scheduleWithFixedDelay(task1, 0, 10, TimeUnit.SECONDS);
Вывод программы, если выполнение задачи занимает 5 секунд.
Задача [App-Task] выполнена: 2022-08-07T15:33:40.853289200Задача [App-Task] выполнена: 2022-08-07T15:33:55.868004500......
При использовании метода scheduleWithFixedRate() выполнение задачи начинается с фиксированного периода задержки. При этом не учитывается, выполняется ли предыдущая задача или нет.
В следующем примере выполнение каждой задачи начнется через 10 секунд. Оно не будет дожидаться завершения предыдущего выполнения.
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);Task task = new Task("App-Task");ScheduledFuture<?> result = executor1.scheduleWithFixedRate(task1, 0, 10, TimeUnit.SECONDS);
Вывод программы, если выполнение задачи занимает 5 секунд.
Задача [App-Task] выполнена: 2022-08-07T15:33:40.853289200Задача [App-Task] выполнена: 2022-08-07T15:33:50.868004500Задача [App-Task] выполнена: 2022-08-07T15:33:60.868007502......
Задавайте мне вопросы, связанные с ScheduledExecutorService, в комментариях.