我有一个方法,我希望使用 Spring运行一次,它需要在给定的java.util.Date(或者替代LocalDateTime)上运行.我计划将该方法应执行的所有日期保留到数据源.它应该异步运行.
一种方法是每天检查DB以获取日期,如果日期已经过去且尚未执行,则执行该方法.有没有更好的办法?
我知道Spring提供了ThreadPoolTaskScheduler和ThreadPoolTaskExecutor.我正在从TaskScheduler接口查看ScheduledFuture调度(Runnable task,Date startTime).我是否需要创建一个Runnable Spring托管bean来调用我的方法?或者是否有更简单的注释可以做到这一点?一个例子真的会有所帮助.
(Looked here too.)
最佳答案 通过将预定日期外部化(到数据库),典型的调度实践(即基于cron或固定调度)不再适用.给定目标日期,您可以准确地安排任务,如下所示:
Date now = new Date();
Date next = ... get next date from external source ...
long delay = next.getTime() - now.getTime();
scheduler.schedule(Runnable task, delay, TimeUnit.MILLISECONDS);
剩下的就是创建一种有效的方法来分派每个新任务.
以下是TaskDispatcher线程,它根据下一个java.util.Date(您从数据库中读取)调度每个Task.没有必要每天检查;这种方法足够灵活,可以与存储在数据库中的任何调度方案一起使用.
接下来是工作代码来说明方法.
使用的示例任务;在这种情况下,只是睡了一段时间.任务完成后,TaskDispatcher将通过CountDownLatch发出信号.
public class Task implements Runnable {
private final CountDownLatch completion;
public Task(CountDownLatch completion) {
this.completion = completion;
}
@Override
public void run() {
System.out.println("Doing task");
try {
Thread.sleep(60*1000); // Simulate the job taking 60 seconds
} catch (InterruptedException e) {
e.printStackTrace();
}
completion.countDown(); // Signal that the job is complete
}
}
调度程序负责读取下一个计划日期的数据库,启动ScheduledFuture runnable,并等待任务完成.
public class TaskDispatcher implements Runnable {
private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private boolean isInterrupted = false;
@Override
public void run() {
while (!isInterrupted) {
Date now = new Date();
System.out.println("Reading database for next date");
Date next = ... read next data from database ...
//Date next = new Date(); // Used as test
//next.setTime(now.getTime()+10*1000); // Used as test
long delay = next.getTime() - now.getTime();
System.out.println("Scheduling next task with delay="+delay);
CountDownLatch latch = new CountDownLatch(1);
ScheduledFuture<?> countdown = scheduler.schedule(new Task(latch), delay, TimeUnit.MILLISECONDS);
try {
System.out.println("Blocking until the current job has completed");
latch.await();
} catch (InterruptedException e) {
System.out.println("Thread has been requested to stop");
isInterrupted = true;
}
if (!isInterrupted)
System.out.println("Job has completed normally");
}
scheduler.shutdown();
}
}
TaskDispatcher启动如下(使用Spring Boot) – 像往常一样使用Spring启动线程:
@Bean
public TaskExecutor taskExecutor() {
return new SimpleAsyncTaskExecutor(); // Or use another one of your liking
}
@Bean
public CommandLineRunner schedulingRunner(TaskExecutor executor) {
return new CommandLineRunner() {
public void run(String... args) throws Exception {
executor.execute(new TaskDispatcher());
}
};
}
如果这种方法适用于您的用例,请告诉我.