当前位置: 首页 > 面试题库 >

Java 如何使用ScheduledExecutorService每天在特定时间运行某些任务?

夏骞尧
2023-03-14
问题内容

我想每天早上5点执行某项任务。因此,我决定使用ScheduledExecutorService它,但到目前为止,我已经看到了一些示例,这些示例显示了如何每隔几分钟运行一次任务。

而且我找不到任何示例来说明如何每天在特定时间(上午5点)每天运行任务,并且还考虑了夏时制的事实-

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

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();
    }
}

有什么办法,我可以ScheduledExecutorService考虑兼顾夏时制的事实,安排一个任务在每天的凌晨5点运行?

而且TimerTask对于这个还是更好ScheduledExecutorService


问题答案:

与当前的Java SE 8版本一样,它具有出色的日期时间API,与java.time使用java.util.Calendarand相比,可以更轻松地完成这些计算 java.util.Date

  • 使用此新API的日期时间类,即LocalDateTime
  • 使用ZonedDateTime类处理时区特定的计算,包括夏令时问题。你将在此处找到教程和示例。
    现在作为使用你的用例计划任务的示例示例:
ZonedDateTime now = ZonedDateTime.now(ZoneId.of("America/Los_Angeles"));
ZonedDateTime nextRun = now.withHour(5).withMinute(0).withSecond(0);
if(now.compareTo(nextRun) > 0)
    nextRun = nextRun.plusDays(1);

Duration duration = Duration.between(now, nextRun);
long initalDelay = duration.getSeconds();

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);            
scheduler.scheduleAtFixedRate(new MyRunnableTask(),
    initalDelay,
    TimeUnit.DAYS.toSeconds(1),
    TimeUnit.SECONDS);

initalDelay计算问调度延迟在执行TimeUnit.SECONDS。对于此用例,单位毫秒及以下的时差问题似乎可以忽略不计。但是你还是可以使用的duration.toMillis(),并TimeUnit.MILLISECONDS以毫秒为单位处理调度computaions

而且TimerTask对此或ScheduledExecutorService更好吗?

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

从@PaddyD,

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

确实如此,@ PaddyD已经给出了解决方法(给他+1),我提供了一个Java8日期时间API和的工作示例ScheduledExecutorService。使用守护程序线程很危险

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是具有功能的接口execute
  • 在停止时ScheduledExecutorService,请始终awaitTermination在调用后使用shutdown:始终有可能你的任务被卡死/死锁,并且用户将永远等待。
    我给Calender给出的上一个示例只是我提到的一个想法,避免了精确的时间计算和夏时制问题。根据@PaddyD的抱怨更新了解决方案


 类似资料:
  • 现在我每天下午3点运行我的cron作业 但是我想一天运行两次cron作业。上午10点30分和下午2点30分 我相信此命令将在上午 10:30 运行。我应该如何在下午 2:30 运行它?

  • 如何将Airflow dag配置为每天在指定时间执行,无论发生什么,就像crons一样。 我知道使用 TimeSensor 可以获得类似的行为,但在这种情况下,这取决于传感器任务,并且可能与 dag 执行时间冲突。 示例:对于传感器方法,如果我让传感器在0小时15分钟运行,但如果dag在稍后执行,则我的任务会延迟,因此即使对于传感器方法,我也需要确保Dag在正确的时间执行。 那么如何确保Dag在指

  • 问题内容: 如何每天在指定时间(例如上午11:00)使用运行任务?我正在使用JDK 1.4.2,我知道它很旧,但这是项目所需要的。 问题答案: Quartz是在Java环境中调度进程的最著名的解决方案,但是您有很多选择。检查此列表: Quartz 是一个开源作业调度系统,可以与几乎任何J2EE或J2SE应用程序集成或一起使用。Quartz可用于创建简单或复杂的时间表。 Jcrontab 旨在扩展并

  • 我一直在尝试让我的不和谐机器人每天在特定时间运行一个功能。目前,机器人每24小时可以做一些事情,所以我需要做的就是让它在特定时间启动。但是,我不知道为什么我不能让它工作。我尝试了多种解决方案,使用时间表、aioscheduler等。我尝试了其他时候的解决方案,这个问题被问到,但我无法让它们工作。 目前,机器人运行,它没有抛出任何错误,但函数roletask()似乎从未被调用。((出于测试目的,Ro

  • 问题内容: 我是Java的新手,我已经开始从事一个项目。但是,我遇到了一个障碍。我需要一种在一天中的特定时间运行的方法。我已经做了很多搜索,但是找不到任何可以解决问题的方法。我遇到了Timer类,但它似乎以一定的间隔运行。Scheduler类似乎也有同样的问题。我也遇到过Quartz,但是我认为我需要更轻巧的东西,而且只能隔一段时间看一下如何做。 也许只是因为我是新手,所以我错过了一些可以在这些课

  • 当我把逻辑放在一个可运行的线程中时,它工作得很好,只是我不能与UI线程交互。所以我试图把所有的东西都放在一个类中,这个类扩展了Task,除了Task只执行一次之外,其他的都可以工作。没有错误,我从Task successed方法获得一条successed消息。 我还尝试在call方法中使task return Boolean为true,但这没有帮助。 请注意,此代码实际上存在于控制器中,但我将其放