Запланируйте новую дату для задачи на каждой итерации - Java Spring - PullRequest
0 голосов
/ 04 января 2019

Мне нужно планировать новую дату для каждого выполнения задачи. Я видел много примеров, когда период или интервал устанавливаются в миллисекундах и остаются для каждой итерации, но я не могу найти ни одного, который принимает параметр даты для следующего выполнения

Я пробовал аннотацию @Scheduled, так как я работаю с Spring, но я не знаю, есть ли возможность передать параметр.

Примеры, которые я видел

Пример 1:

@Scheduled(fixedRate = 20000)
    public void scheduler() {
        log.info("scheduler");
        log.info("Current Thread " + Thread.currentThread().getName());
        log.info("Current Thread " + Thread.currentThread().getId());
}

Пример 2:

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
        scheduler.scheduleAtFixedRate(myRunnable, 10, 10, TimeUnit.MINUTES);

Я ожидаю прочитать Date из таблицы базы данных, чтобы запланировать мою задачу для новой итерации

Ты за помощь!

Редактировать

Примечание: будет также время, когда мне нужно будет решить, где остановить следующую итерацию, поэтому я пытаюсь вызвать задачу расписания с помощью метода

Ответы [ 2 ]

0 голосов
/ 05 января 2019

Я избегаю использования Spring, поэтому не могу вам помочь. Но я могу помочь вам использовать ScheduledExecutorService для достижения вашей цели.

ScheduledExecutorService::schedule​( Runnable command, long delay, TimeUnit unit )

Вы частично правы относительно ScheduledExecutorService: две из трех его стратегий планирования предназначены для поддержания регулярных интервалов между прогонами:

Но третья стратегия позволяет вам установить следующий прогон с любой задержкой, которую вы пожелаете.

Если вы хотите, чтобы одна задача выполнялась многократно, но не одновременно, используйте однопотоковый исполнитель .

ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor() ;

На этом ScheduledExecutorService, запланируйте свою задачу. И сделайте последний шаг этой задачи рутинной задачей планирования следующего события. У нас есть вечный двигатель, каждый раз, когда задача запускается, она планирует следующий запуск на неопределенный срок.

Определите вашу задачу Runnable.

    Runnable runnable = new Runnable() {
        @Override
        public void run ( ) {
            // Do the work of this task.
            ZonedDateTime zdt = ZonedDateTime.now( ZoneId.systemDefault() ); // Capture the current moment.
            System.out.println( "Current moment: " + zdt ); // Report the current moment.
            // Schedule the next run of this task.
            scheduledExecutorService.schedule( this , 10L , TimeUnit.SECONDS );  // Delay will not be *exactly* this amount of time due to interruptions of scheduling cores on CPU and threads by the JVM and host OS.
        }
    };

Тогда запустите.

    // Jump-start this perpetual motion machine.
    scheduledExecutorService.schedule( runnable , 0L , TimeUnit.SECONDS );  // Start immediately, no delay.

Пусть исполнитель выполняет свою работу повторно в течение определенного периода времени. Спите основной поток, пока служба исполнителя работает в фоновом потоке.

    try {
        Thread.sleep( TimeUnit.MINUTES.toMillis( 2 ) );  // Let our app, and the executor, run for 2 minutes, then shut them both down.
    } catch ( InterruptedException e ) {
        e.printStackTrace();
    }

Не забудьте всегда выключать исполнителя . В противном случае его фоновые потоки могут продолжаться долго после выхода из основного приложения.

    scheduledExecutorService.shutdown();
    System.out.println( "INFO - Executor shutting down. App exiting. " + ZonedDateTime.now( ZoneId.systemDefault() ) );

Совет. Всегда включайте код Runnable во все исключения. Любое неперехваченное исключение, достигающее службы executor, приведет к немедленной остановке executor и молча.

    Runnable runnable = new Runnable() {
        @Override
        public void run ( ) {
            try {
                // Do the work of this task.
                ZonedDateTime zdt = ZonedDateTime.now( ZoneId.systemDefault() ); // Capture the current moment.
                System.out.println( "Current moment: " + zdt ); // Report the current moment.
                // Schedule the next run of this task.
                scheduledExecutorService.schedule( this , 10L , TimeUnit.SECONDS );  // Delay will not be *exactly* this amount of time due to interruptions of scheduling cores on CPU and threads by the JVM and host OS.
            } catch ( Exception e ) {
                // TODO: Handle unexpected exeption.
                System.out.println( "ERROR - unexpected exception caught on its way to reaching a scheduled executor service. Message # 55cbae82-8492-4638-9630-60c5b28ad876." );
            }
        }
    };

Я ожидаю прочитать Date из таблицы БД, чтобы запланировать мою задачу для новой итерации

Никогда не используйте Date или Calendar. Эти ужасные классы были вытеснены несколько лет назад java.time с принятием JSR 310.

Начиная с JDBC 4.2 и выше, мы можем напрямую обмениваться java.time объектами с базой данных.

OffsetDateTime now = OffsetDateTime.now( ZoneOffset.UTC ) ;
OffsetDateTime later = myResultSet.getObject( … , OffsetDateTime.class ) ;
if( ! now.isBefore( later ) ) { … } // Verify the future moment is indeed in the future.

Рассчитать прошедшее время, количество времени, которое мы хотим отложить до следующего запланированного запуска.

Duration d = Duration.between( now , odt ) ;
long seconds = d.toSeconds() ; // Truncates any fractional second.

Используйте это количество секунд для планирования следующего запуска.

scheduledExecutorService.schedule( this , seconds , TimeUnit.SECONDS ); 

Так что Runnable теперь выглядит так.

    Runnable runnable = new Runnable() {
        @Override
        public void run ( ) {
            try {
                // Do the work of this task.
                ZonedDateTime zdt = ZonedDateTime.now( ZoneId.systemDefault() ); // Capture the current moment.
                System.out.println( "Current moment: " + zdt ); // Report the current moment.
                // Schedule the next run of this task.
                OffsetDateTime now = OffsetDateTime.now( ZoneOffset.UTC ) ;
                … do your database query …
                OffsetDateTime later = myResultSet.getObject( … , OffsetDateTime.class ) ;
                if( ! now.isBefore( later ) ) { … } // Verify the future moment is indeed in the future.
                Duration d = Duration.between( now , odt ) ;
                long seconds = d.toSeconds() ; // Truncates any fractional second.
                scheduledExecutorService.schedule( this , seconds , TimeUnit.SECONDS );  // Delay will not be *exactly* this amount of time due to interruptions of scheduling cores on CPU and threads by the JVM and host OS.
            } catch ( Exception e ) {
                // TODO: Handle unexpected exeption.
                System.out.println( "ERROR - unexpected exception caught on its way to reaching a scheduled executor service. Message # 55cbae82-8492-4638-9630-60c5b28ad876." );
            }
        }
    };

Вот полный пример в одном файле .java, но без запроса к базе данных.

package work.basil.example;

import java.util.concurrent.*;
import java.time.*;

public class ScheduleNextTaskExample {
    public static void main ( String[] args ) {
        ScheduleNextTaskExample app = new ScheduleNextTaskExample();
        app.doIt();
    }

    private void doIt ( ) {
        ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();

        Runnable runnable = new Runnable() {
            @Override
            public void run ( ) {
                try {
                    ZonedDateTime zdt = ZonedDateTime.now( ZoneId.systemDefault() ); // Capture the current moment.
                    System.out.println( "Current moment: " + zdt ); // Report the current moment.
                    scheduledExecutorService.schedule( this , 10L , TimeUnit.SECONDS );  // Delay will not be *exactly* this amount of time due to interruptions of scheduling cores on CPU and threads by the JVM and host OS.
                } catch ( Exception e ) {
                    // TODO: Handle unexpected exeption.
                    System.out.println( "ERROR - unexpected exception caught on its way to reaching a scheduled executor service. Message # 55cbae82-8492-4638-9630-60c5b28ad876." );
                }
            }
        };

        // Jump-start this perpetual motion machine.
        scheduledExecutorService.schedule( runnable , 0L , TimeUnit.SECONDS );  // Start immediately, no delay.
        try {
            Thread.sleep( TimeUnit.MINUTES.toMillis( 2 ) );  // Let our app, and the executor, run for 2 minutes, then shut them both down.
        } catch ( InterruptedException e ) {
            e.printStackTrace();
        }
        scheduledExecutorService.shutdown();
        System.out.println( "INFO - Executor shutting down. App exiting. " + ZonedDateTime.now( ZoneId.systemDefault() ) );

    }
}
0 голосов
/ 04 января 2019

Вы можете зарегистрировать новую TimerTask, выполнить желаемую логику и зарегистрировать новую TimerTask по завершении желаемой логики:

public class Starter {

    public void execute() {
        Timer timer = new Timer();
        Date firstExecutionDate = // ... compute ...
        timer.schedule(
            new RepeatedTimerTask(timer, this::toDoUponEachExecution, this::findNextExecutionDate),
            firstExecutionDate
        );
    }

    private Date findNextExecutionDate() {
        // ... compute ...
    }

    private void toDoUponEachExecution() {
        // ... do something ...
    }
}

public class RepeatedTimerTask extends TimerTask {

    private final Timer timer;
    private final Runnable logic;
    private final Supplier<Date> nextExecution;

    public RepeatedTimerTask(Timer timer, Runnable logic, Supplier<Date> nextExecution) {
        this.timer = timer;
        this.logic = logic;
        this.nextExecution = nextExecution;
    }

    @Override
    public void run() {
        logic.run();
        timer.schedule(this, nextExecution.get());
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...