Рубрики
Без рубрики

Планировщик Java

Планировщик Java и динамическое планирование

Автор оригинала: 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”