Автор оригинала: Joydip Kumar.
В этой статье мы рассмотрим следующие темы о планировщике:
- Планировщик на Java
- Планировщик Настройка rvs @По расписанию
- Динамическое изменение выражения cron
- выполнение зависимостей между двумя задачами.
Планирование задачи на Java
Планировщик предназначен для планирования выполнения потока или задачи в определенный период времени или периодически с фиксированным интервалом . В Java существует несколько способов планирования задачи .
- Библиотека Java java.util. Временная задача
- java.util.параллельный. ScheduledExecutorService
- Кварцевый планировщик
- организация.весенняя работа.планирование. Планировщик задач
Временная задача выполняется одним потоком демона выполнения. Любая задержка в выполнении задачи может привести к задержке выполнения другой задачи в расписании . Следовательно, это нецелесообразный вариант, когда несколько задач должны выполняться асинхронно в определенное время .
Давайте возьмем пример:
package com.example.timerExamples; import java.util.Timer; public class ExecuteTimer { public static void main(String[] args){ TimerExample te1=new TimerExample("Task1"); TimerExample te2=new TimerExample("Task2"); Timer t=new Timer(); t.scheduleAtFixedRate(te1, 0,5*1000); t.scheduleAtFixedRate(te2, 0,1000); } }
public class TimerExample extends TimerTask{ private String name ; public TimerExample(String n){ this.name=n; } @Override public void run() { System.out.println(Thread.currentThread().getName()+" "+name+" the task has executed successfully "+ new Date()); if("Task1".equalsIgnoreCase(name)){ try { Thread.sleep(10000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
Выход:
Timer-0 Task1 the task has executed successfully Wed Nov 14 14:32:49 GMT 2018 Timer-0 Task2 the task has executed successfully Wed Nov 14 14:32:59 GMT 2018 Timer-0 Task2 the task has executed successfully Wed Nov 14 14:32:59 GMT 2018 Timer-0 Task2 the task has executed successfully Wed Nov 14 14:32:59 GMT 2018 Timer-0 Task2 the task has executed successfully Wed Nov 14 14:32:59 GMT 2018 Timer-0 Task2 the task has executed successfully Wed Nov 14 14:32:59 GMT 2018 Timer-0 Task2 the task has executed successfully Wed Nov 14 14:32:59 GMT 2018 Timer-0 Task1 the task has executed successfully Wed Nov 14 14:32:59 GMT 2018 Timer-0 Task2 the task has executed successfully Wed Nov 14 14:33:09 GMT 2018 Timer-0 Task2 the task has executed successfully Wed Nov 14 14:33:09 GMT 2018 Timer-0 Task2 the task has executed successfully Wed Nov 14 14:33:09 GMT 2018 Timer-0 Task2 the task has executed successfully Wed Nov 14 14:33:09 GMT 2018 Timer-0 Task2 the task has executed successfully Wed Nov 14 14:33:09 GMT 2018 Timer-0 Task1 the task has executed successfully Wed Nov 14 14:33:09 GMT 2018
В приведенном выше выполнении ясно, что задача 2 застревает, потому что поток, обрабатывающий задачу 1, переходит в спящий режим на 10 секунд. Следовательно, существует только один поток демонов, который работает как над задачей 1, так и над задачей 2 и если кто-то получит удар, вся задача будет отодвинута назад .
ScheduledExecutorService и планировщик задач работают одинаково . Единственная разница в том, что первая-это библиотека Java, а последняя-spring framework . Поэтому , если приложение работает весной, планировщик задач может быть лучшим вариантом для планирования заданий .
Теперь давайте посмотрим, как используется интерфейс планировщика задач, и мы сможем использовать его весной .
Планировщик Настройка rvs @По расписанию
Spring предоставляет планирование на основе аннотаций с помощью @Scheduled .
Потоки обрабатываются Spring framework, и у нас не будет никакого контроля над потоками, которые будут работать над задачами . Давайте рассмотрим приведенный ниже пример:
@Configuration @EnableScheduling public class ScheduledConfiguration { @Scheduled(fixedRate = 5000) public void executeTask1() { System.out.println(Thread.currentThread().getName()+" The Task1 executed at "+ new Date()); try { Thread.sleep(10000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Scheduled(fixedRate = 1000) public void executeTask2() { System.out.println(Thread.currentThread().getName()+" The Task2 executed at "+ new Date()); } }
Выход:
scheduling-1 The Task2 executed at Wed Nov 14 14:22:59 GMT 2018 scheduling-1 The Task2 executed at Wed Nov 14 14:22:59 GMT 2018 scheduling-1 The Task2 executed at Wed Nov 14 14:22:59 GMT 2018 scheduling-1 The Task2 executed at Wed Nov 14 14:22:59 GMT 2018 scheduling-1 The Task2 executed at Wed Nov 14 14:22:59 GMT 2018 scheduling-1 The Task1 executed at Wed Nov 14 14:22:59 GMT 2018 scheduling-1 The Task2 executed at Wed Nov 14 14:23:09 GMT 2018 scheduling-1 The Task2 executed at Wed Nov 14 14:23:09 GMT 2018 scheduling-1 The Task2 executed at Wed Nov 14 14:23:09 GMT 2018 scheduling-1 The Task2 executed at Wed Nov 14 14:23:09 GMT 2018 scheduling-1 The Task2 executed at Wed Nov 14 14:23:09 GMT 2018 scheduling-1 The Task1 executed at Wed Nov 14 14:23:09 GMT 2018
Существует одно планирование потоков-1, которое обрабатывает как задачу 1, так и задачу 2 . В тот момент , когда задача 1 переходит в спящий режим на 10 секунд, задача 2 также ожидает ее . Следовательно , мы понимаем, что если одновременно выполняются 2 задания, одно из них будет ждать завершения другого .
Теперь мы попробуем написать задачу планировщика, в которой мы хотим выполнять задачи 1 и 2 асинхронно . Там будет пул потоков, и мы будем планировать каждую задачу в ThreadPoolTaskScheduler . Класс должен реализовать интерфейс SchedulingConfigurer . Это дает больший контроль над потоками планировщика по сравнению с @Scheduled .
@Configuration @EnableScheduling public class ScheduledConfiguration implements SchedulingConfigurer { TaskScheduler taskScheduler; private ScheduledFuture> job1; private ScheduledFuture> job2; @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { ThreadPoolTaskScheduler threadPoolTaskScheduler =new ThreadPoolTaskScheduler(); threadPoolTaskScheduler.setPoolSize(10);// Set the pool of threads threadPoolTaskScheduler.setThreadNamePrefix("scheduler-thread"); threadPoolTaskScheduler.initialize(); job1(threadPoolTaskScheduler);// Assign the job1 to the scheduler job2(threadPoolTaskScheduler);// Assign the job1 to the scheduler this.taskScheduler=threadPoolTaskScheduler;// this will be used in later part of the article during refreshing the cron expression dynamically taskRegistrar.setTaskScheduler(threadPoolTaskScheduler); } private void job1(TaskScheduler scheduler) { job1 = scheduler.schedule(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + " The Task1 executed at " + new Date()); try { Thread.sleep(10000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }, new Trigger() { @Override public Date nextExecutionTime(TriggerContext triggerContext) { String cronExp = "0/5 * * * * ?";// Can be pulled from a db . return new CronTrigger(cronExp).nextExecutionTime(triggerContext); } }); } private void job2(TaskScheduler scheduler){ job2=scheduler.schedule(new Runnable(){ @Override public void run() { System.out.println(Thread.currentThread().getName()+" The Task2 executed at "+ new Date()); } }, new Trigger(){ @Override public Date nextExecutionTime(TriggerContext triggerContext) { String cronExp="0/1 * * * * ?";//Can be pulled from a db . This will run every minute return new CronTrigger(cronExp).nextExecutionTime(triggerContext); } }); } }
Выход:
scheduler-thread1 The Task2 executed at Wed Nov 14 15:02:46 GMT 2018 scheduler-thread2 The Task2 executed at Wed Nov 14 15:02:47 GMT 2018 scheduler-thread3 The Task2 executed at Wed Nov 14 15:02:48 GMT 2018 scheduler-thread2 The Task2 executed at Wed Nov 14 15:02:49 GMT 2018 scheduler-thread1 The Task2 executed at Wed Nov 14 15:02:50 GMT 2018 scheduler-thread7 The Task1 executed at Wed Nov 14 15:02:50 GMT 2018 scheduler-thread3 The Task2 executed at Wed Nov 14 15:02:51 GMT 2018 scheduler-thread5 The Task2 executed at Wed Nov 14 15:02:52 GMT 2018 scheduler-thread2 The Task2 executed at Wed Nov 14 15:02:53 GMT 2018 scheduler-thread6 The Task2 executed at Wed Nov 14 15:02:54 GMT 2018 scheduler-thread6 The Task2 executed at Wed Nov 14 15:02:55 GMT 2018 scheduler-thread6 The Task2 executed at Wed Nov 14 15:02:56 GMT 2018 scheduler-thread6 The Task2 executed at Wed Nov 14 15:02:57 GMT 2018 scheduler-thread6 The Task2 executed at Wed Nov 14 15:02:58 GMT 2018 scheduler-thread6 The Task2 executed at Wed Nov 14 15:02:59 GMT 2018 scheduler-thread6 The Task2 executed at Wed Nov 14 15:03:00 GMT 2018 scheduler-thread2 The Task2 executed at Wed Nov 14 15:03:01 GMT 2018 scheduler-thread2 The Task2 executed at Wed Nov 14 15:03:02 GMT 2018 scheduler-thread2 The Task2 executed at Wed Nov 14 15:03:03 GMT 2018 scheduler-thread2 The Task2 executed at Wed Nov 14 15:03:04 GMT 2018 scheduler-thread10 The Task2 executed at Wed Nov 14 15:03:05 GMT 2018 scheduler-thread8 The Task1 executed at Wed Nov 14 15:03:05 GMT 2018
Я создаю 2 рабочих места: job1 и job2 . Затем запланируйте его с помощью планировщика задач. На этот раз я использую выражение Cron для планирования задания 1 каждые 5 секунд и задания 2 каждую секунду . Задание 1 застревает на 10 секунд, и мы увидим, что задание 2 по-прежнему работает без сбоев . Мы видим, что и задача 1, и задача 2 обрабатываются пулом потоков, который создается с помощью ThreadPoolTaskScheduler
Динамическое изменение выражения cron
мы всегда можем сохранить выражение cron в файле свойств в конфигурации Spring . Если сервер конфигурации Spring недоступен , мы также можем получить его из базы данных . Любое обновление в выражении cron обновит планировщик . Но для того, чтобы отменить текущее расписание и выполнить новое расписание, мы можем предоставить API для обновления задания cron:
public void refreshCronSchedule(){ if(job1!=null){ job1.cancel(true); scheduleJob1(taskScheduler); } if(job2!=null){ job2.cancel(true); scheduleJob2(taskScheduler); } }
Вызовите метод с любого контроллера, чтобы обновить расписание cron.
Выполнение зависимостей между двумя задачами.
До сих пор мы знали, что можем выполнять задания асинхронно, используя планировщик задач и интерфейс Schedulingconfigurer. Теперь предположим, что у нас есть задание 1, которое выполняется в течение часа в 1 час ночи, и у нас есть задание 2, которое должно выполняться в 2 часа ночи . Но задание 2 не должно начинаться, пока задание 1 не будет завершено . У нас также есть другой список заданий, которые могут выполняться с 1 до 2 часов ночи и не зависят от других заданий .
Давайте посмотрим , как мы можем создать зависимость между заданиями 1 и 2, но при этом запускать все задания асинхронно в запланированное время .
объявите изменчивую переменную:
частное изменчивое логическое значение;
private void scheduleJob1(TaskScheduler scheduler) { job1 = scheduler.schedule(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + " The Task1 executed at " + new Date()); try { Thread.sleep(10000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } job1Flag=true;// setting the flag true to mark it complete } }, new Trigger() { @Override public Date nextExecutionTime(TriggerContext triggerContext) { String cronExp = "0/5 * * * * ?";// Can be pulled from a db return new CronTrigger(cronExp).nextExecutionTime(triggerContext); } }); }
private void scheduleJob2(TaskScheduler scheduler) { job2=scheduler.schedule(new Runnable(){ @Override public void run() { synchronized(this){ while(!job1Flag){ System.out.println(Thread.currentThread().getName()+" waiting for job1 to complete to execute "+ new Date()); try { wait(1000);// add any number of seconds to wait } catch (InterruptedException e) { e.printStackTrace(); } } } System.out.println(Thread.currentThread().getName()+" The Task2 executed at "+ new Date()); job1Flag=false; } }, new Trigger(){ @Override public Date nextExecutionTime(TriggerContext triggerContext) { String cronExp="0/5 * * * * ?";//Can be pulled from a db . This will run every minute return new CronTrigger(cronExp).nextExecutionTime(triggerContext); } }); }
scheduler-thread2 The Task1 executed at Wed Nov 14 16:30:50 GMT 2018 scheduler-thread1 waiting for job1 to complete to execute Wed Nov 14 16:30:51 GMT 2018 scheduler-thread1 waiting for job1 to complete to execute Wed Nov 14 16:30:52 GMT 2018 scheduler-thread1 waiting for job1 to complete to execute Wed Nov 14 16:30:53 GMT 2018 scheduler-thread1 waiting for job1 to complete to execute Wed Nov 14 16:30:54 GMT 2018 scheduler-thread1 waiting for job1 to complete to execute Wed Nov 14 16:30:55 GMT 2018 scheduler-thread1 waiting for job1 to complete to execute Wed Nov 14 16:30:56 GMT 2018 scheduler-thread1 waiting for job1 to complete to execute Wed Nov 14 16:30:57 GMT 2018 scheduler-thread1 waiting for job1 to complete to execute Wed Nov 14 16:30:58 GMT 2018 scheduler-thread1 waiting for job1 to complete to execute Wed Nov 14 16:30:59 GMT 2018 scheduler-thread1 The Task2 executed at Wed Nov 14 16:31:00 GMT 2018 scheduler-thread2 The Task1 executed at Wed Nov 14 16:31:05 GMT 2018 scheduler-thread3 waiting for job1 to complete to execute Wed Nov 14 16:31:05 GMT 2018 scheduler-thread3 waiting for job1 to complete to execute Wed Nov 14 16:31:06 GMT 2018 scheduler-thread3 waiting for job1 to complete to execute Wed Nov 14 16:31:07 GMT 2018 scheduler-thread3 waiting for job1 to complete to execute Wed Nov 14 16:31:08 GMT 2018 scheduler-thread3 waiting for job1 to complete to execute Wed Nov 14 16:31:09 GMT 2018 scheduler-thread3 waiting for job1 to complete to execute Wed Nov 14 16:31:10 GMT 2018 scheduler-thread3 waiting for job1 to complete to execute Wed Nov 14 16:31:11 GMT 2018 scheduler-thread3 waiting for job1 to complete to execute Wed Nov 14 16:31:12 GMT 2018 scheduler-thread3 waiting for job1 to complete to execute Wed Nov 14 16:31:13 GMT 2018 scheduler-thread3 waiting for job1 to complete to execute Wed Nov 14 16:31:14 GMT 2018 scheduler-thread3 The Task2 executed at Wed Nov 14 16:31:15 GMT 2018 scheduler-thread1 The Task1 executed at Wed Nov 14 16:31:20 GMT 2018
Использование изменчивого логического флага, чтобы он не кэшировался в локальном потоке, а сохранялся в основной памяти и мог использоваться всеми потоками в пуле . Основываясь на флаге , задание 2 ожидает неопределенно долго, пока задание 1 не завершится . Теперь, если задание 1 зависнет, есть вероятность, что задание 2 будет ждать бесконечно .
Вывод
Существуют различные способы планирования, и теперь мы знаем, когда мы можем использовать планировщик spring и API планировщика java . И как использовать API планировщика spring и управлять потоками в пуле .
Оригинал: “https://www.codementor.io/@joydipkumar/java-scheduler-xudhcr72w”