如何使用ScheduledExecutorService在特定时间每天运行某些任务? [英] How to run certain task every day at a particular time using ScheduledExecutorService?

查看:1148
本文介绍了如何使用ScheduledExecutorService在特定时间每天运行某些任务?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我每天凌晨5点都在尝试完成某项任务。所以我决定使用 ScheduledExecutorService ,但到目前为止,我已经看到了示例,显示了如何每隔几分钟运行一次任务。

I am trying to run a certain task everyday at 5 AM in the morning. So I decided to use ScheduledExecutorService for this but so far I have seen examples which shows how to run task every few minutes.

我无法找到任何示例,说明如何在早上的特定时间(早上5点)每天运行一项任务,同时考虑到夏令时也是 -

And I am not able to find any example which shows how to run a task every day at a particular time (5 AM) in the morning and also considering the fact of daylight saving time as well -

以下是我的代码,每15分钟运行一次 -

Below is my code which will run every 15 minutes -

public class ScheduledTaskExample {
    private final ScheduledExecutorService scheduler = Executors
        .newScheduledThreadPool(1);

    public void startScheduleTask() {
    /**
    * not using the taskHandle returned here, but it can be used to cancel
    * the task, or check if it's done (for recurring tasks, that's not
    * going to be very useful)
    */
    final ScheduledFuture<?> taskHandle = scheduler.scheduleAtFixedRate(
        new Runnable() {
            public void run() {
                try {
                    getDataFromDatabase();
                }catch(Exception ex) {
                    ex.printStackTrace(); //or loggger would be better
                }
            }
        }, 0, 15, TimeUnit.MINUTES);
    }

    private void getDataFromDatabase() {
        System.out.println("getting data...");
    }

    public static void main(String[] args) {
        ScheduledTaskExample ste = new ScheduledTaskExample();
        ste.startScheduleTask();
    }
}

有什么办法,我可以安排任务到每天早上5点运行 ScheduledExecutorService 考虑夏令时的事实?

Is there any way, I can schedule a task to run every day 5 AM in the morning using ScheduledExecutorService considering the fact of daylight saving time as well?

还有 TimerTask 更适合这个或 ScheduledExecutorService

推荐答案

与目前的Java SE 8版本一样,它的优秀日期时间API与 java.time 相比,这些计算可以更容易地完成使用 java.util.Calendar java.util.Date

As with the present java SE 8 release with it's excellent date time API with java.time these kind of calculation can be done more easily instead of using java.util.Calendar and java.util.Date.

  • Use date time class's i.e. LocalDateTime of this new API
  • Use ZonedDateTime class to handle Time Zone specific calculation including Daylight Saving issues. You will find tutorial and example here.

现在作为使用您的用例安排任务的示例示例:

Now as a sample example for scheduling a task with your use case:

        LocalDateTime localNow = LocalDateTime.now();
        ZoneId currentZone = ZoneId.of("America/Los_Angeles");
        ZonedDateTime zonedNow = ZonedDateTime.of(localNow, currentZone);
        ZonedDateTime zonedNext5 ;
        zonedNext5 = zonedNow.withHour(5).withMinute(0).withSecond(0);
        if(zonedNow.compareTo(zonedNext5) > 0)
            zonedNext5 = zonedNext5.plusDays(1);

        Duration duration = Duration.between(zonedNow, zonedNext5);
        long initalDelay = duration.getSeconds();

        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);            
        scheduler.scheduleAtFixedRate(new MyRunnableTask(), initalDelay,
                                      24*60*60, TimeUnit.SECONDS);

计算 initalDelay 以询问调度程序延迟 TimeUnit.SECONDS 中的执行。对于此用例,单位毫秒及以下的时差问题似乎可以忽略不计。但您仍然可以使用 duration.toMillis() TimeUnit.MILLISECONDS 来处理以毫秒为单位的调度计算。

The initalDelay is computed to ask the scheduler to delay the execution in TimeUnit.SECONDS. Time difference issues with unit milliseconds and below seems to be negligible for this use case. But you can still make use of duration.toMillis() and TimeUnit.MILLISECONDS for handling the scheduling computaions in milliseconds.


而且TimerTask对于这个或者ScheduledExecutorService更好吗?

And also TimerTask is better for this or ScheduledExecutorService?

NO: ScheduledExecutorService 似乎比 TimerTask 更好。 StackOverflow已经为您提供了答案

NO: ScheduledExecutorService seemingly better than TimerTask. StackOverflow has already an answer for you.

来自@PaddyD,


您仍然遇到需要的问题如果您希望它在当地时间运行,则每年重启两次
。 scheduleAtFixedRate
不会削减它,除非你对全年相同的UTC时间感到满意。

You still have the issue whereby you need to restart this twice a year if you want it to run at the right local time. scheduleAtFixedRate won't cut it unless you are happy with the same UTC time all year.

因为它是真的, @PaddyD已经给出了一个解决方法(给他+1),我提供了一个带有 ScheduledExecutorService 的Java8日期时间API的工作示例。 使用守护程序线程是危险的

As it is true and @PaddyD already has given a workaround(+1 to him), I am providing a working example with Java8 date time API with ScheduledExecutorService. Using daemon thread is dangerous

class MyTaskExecutor
{
    ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
    MyTask myTask;
    volatile boolean isStopIssued;

    public MyTaskExecutor(MyTask myTask$) 
    {
        myTask = myTask$;

    }

    public void startExecutionAt(int targetHour, int targetMin, int targetSec)
    {
        Runnable taskWrapper = new Runnable(){

            @Override
            public void run() 
            {
                myTask.execute();
                startExecutionAt(targetHour, targetMin, targetSec);
            }

        };
        long delay = computeNextDelay(targetHour, targetMin, targetSec);
        executorService.schedule(taskWrapper, delay, TimeUnit.SECONDS);
    }

    private long computeNextDelay(int targetHour, int targetMin, int targetSec) 
    {
        LocalDateTime localNow = LocalDateTime.now();
        ZoneId currentZone = ZoneId.systemDefault();
        ZonedDateTime zonedNow = ZonedDateTime.of(localNow, currentZone);
        ZonedDateTime zonedNextTarget = zonedNow.withHour(targetHour).withMinute(targetMin).withSecond(targetSec);
        if(zonedNow.compareTo(zonedNextTarget) > 0)
            zonedNextTarget = zonedNextTarget.plusDays(1);

        Duration duration = Duration.between(zonedNow, zonedNextTarget);
        return duration.getSeconds();
    }

    public void stop()
    {
        executorService.shutdown();
        try {
            executorService.awaitTermination(1, TimeUnit.DAYS);
        } catch (InterruptedException ex) {
            Logger.getLogger(MyTaskExecutor.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

注意:


  • MyTask 是一个功能的界面执行

  • 在停止 ScheduledExecutorService 时,在调用<$ c $后始终使用 awaitTermination c>关闭:它总是有可能你的任务卡住/死锁,用户会永远等待。

  • MyTask is an interface with function execute.
  • While stopping ScheduledExecutorService, Always use awaitTermination after invoking shutdown on it: There's always a likelihood your task is stuck / deadlocking and the user would wait forever.

我之前给Calender的例子只是一个想法我提到过,我避免了精确的时间计算和夏令时问题。根据@PaddyD

The previous example I gave with Calender was just an idea which I did mention, I avoided exact time calculation and Daylight saving issues. Updated the solution on per the complain of @PaddyD

这篇关于如何使用ScheduledExecutorService在特定时间每天运行某些任务?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆