Я избегаю использования 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() ) );
}
}