I need to schedule to a new Date for everytime a task it's executed. I've seen many examples where the period or interval is set by millis and stays for every iteration but i can't find any that takes a date parameter for next execution
I tried the @Scheduled annotation since I am working with Spring, but I do not know if there is a possibility to pass a parameter.
Examples i've seen
Example 1:
@Scheduled(fixedRate = 20000)
public void scheduler() {
log.info("scheduler");
log.info("Current Thread " + Thread.currentThread().getName());
log.info("Current Thread " + Thread.currentThread().getId());
}
Example 2:
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(myRunnable, 10, 10, TimeUnit.MINUTES);
I expect to read a Date from a db table to schedule my task for new iteration
Ty for the help!
Edit
Note: There will also be a time when I need to decide where to stop the next iteration, so I'm trying to call the schedule task by a method
I avoid using Spring, so I cannot help you there. But I can guide you through the use of ScheduledExecutorService
to accomplish your goal.
ScheduledExecutorService::schedule( Runnable command, long delay, TimeUnit unit )
You are partially correct about the ScheduledExecutorService
: Two of its three scheduling strategies are designed to keep regular intervals between runs:
But the third strategy lets you set the next run with any amount of delay you wish.
schedule( Runnable command, long delay, TimeUnit unit )
schedule( Callable<V> callable, long delay, TimeUnit unit )
If you want a single task to be executed repeatedly but not concurrently, use a single-thread executor.
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor() ;
On that ScheduledExecutorService
, schedule your task. And make the last step of that task be the chore of scheduling the next occurrence. We have a perpetual motion machine, each time the task runs, it schedules the next run, indefinitely.
Define your Runnable
task.
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.
}
};
Then run it.
// Jump-start this perpetual motion machine.
scheduledExecutorService.schedule( runnable , 0L , TimeUnit.SECONDS ); // Start immediately, no delay.
Let the executor do its work repeatedly for a certain length of time. Sleep the main thread while the executor service runs on a background thread(s).
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();
}
Remember to always shutdown the executor. Otherwise its background thread(s) may continue running long after your main app has exited.
scheduledExecutorService.shutdown();
System.out.println( "INFO - Executor shutting down. App exiting. " + ZonedDateTime.now( ZoneId.systemDefault() ) );
Tip: Always wrap your Runnable
code in a try-catch for all exceptions. Any uncaught exception reaching the executor service will cause the executor to immediately halt, and halt silently.
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." );
}
}
};
I expect to read a Date from a db table to schedule my task for new iteration
Never use Date
or Calendar
. Those terrible classes were supplanted years ago by the java.time with the adoption of JSR 310.
As of JDBC 4.2 and later, we can directly exchange java.time objects with the database.
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.
Calculate elapsed time, the amount of time we want to delay until our next scheduled run.
Duration d = Duration.between( now , odt ) ;
long seconds = d.toSeconds() ; // Truncates any fractional second.
Use that number of seconds to schedule the next run.
scheduledExecutorService.schedule( this , seconds , TimeUnit.SECONDS );
So the Runnable
now looks like this.
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." );
}
}
};
Here is the complete example in a single .java
file but without the database query.
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() ) );
}
}