使用ThreadPoolTask​​Executor在特定日期调用方法

时间:2017-05-17 22:54:02

标签: spring

我有一个方法,我希望使用Spring运行一次,它需要在给定的java.util.Date(或LocalDateTime)上运行。我计划将该方法应执行的所有日期保留到数据源。它应该异步运行。

一种方法是每天检查DB以获取日期,如果日期已经过去并且尚未执行,则执行该方法。还有更好的方法吗?

我知道Spring提供ThreadPoolTaskSchedulerThreadPoolTaskExecutor。我正在查看ScheduledFuture schedule(Runnable task, Date startTime)界面中的TaskScheduler。我是否需要创建一个Runnable Spring托管bean来调用我的方法?或者是否有更简单的注释可以做到这一点?一个例子真的会有所帮助。

Looked here too.

2 个答案:

答案 0 :(得分:2)

通过外部化预定日期(到数据库),典型的调度实践(即基于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());
        }
    };
}

请告诉我这种方法是否适用于您的用例。

答案 1 :(得分:0)

看看the @Scheduled annotation。它可以完成你正在寻找的东西。

@Scheduled(cron="*/5 * * * * MON-FRI")
public void scheduledDateWork() {
    Date date = new Date(); //or use DAO call to look up date in database

    executeLogic(date);
}

Cron表达示例from another answer

"0 0 * * * *" = the top of every hour of every day.
"*/10 * * * * *" = every ten seconds.
"0 0 8-10 * * *" = 8, 9 and 10 o'clock of every day.
"0 0/30 8-10 * * *" = 8:00, 8:30, 9:00, 9:30 and 10 o'clock every day.
"0 0 9-17 * * MON-FRI" = on the hour nine-to-five weekdays
"0 0 0 25 12 ?" = every Christmas Day at midnight