在时间段内每30分钟运行一次任务

时间:2020-04-06 21:14:21

标签: java

我对Java还是很陌生,但是我正尝试在一周的凌晨5点至下午5点之间每30分钟执行一次任务。我环顾四周,发现可以使用此类

  ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();

我想我可以使用该库来调用函数,并将函数调用包装在if语句中,以检查它是否介于9到5时间之间。关于此的我唯一的问题是在整个晚上不执行任何操作时运行线程。想知道这是否是我应该担心的事情,还是有一种方法可以让线程在上次运行8个小时后直到第二天进入睡眠状态

1 个答案:

答案 0 :(得分:1)

是的,请使用ScheduledExecutorService

请注意,schedule方法如何使用一对参数来获取运行任务之前要等待的时间量和粒度。

因此,当您启动应用程序时,请查看要开始第一个任务的时间。使用DayOfWeek查看当前时刻是否为星期一至星期五。检查当前的LocalTime是否在5 AM到5 PM之间。使用ZonedDateTime确定时刻。

要获取下一个工作日,请将ThreeTen-Extra库添加到您的项目中,以获取其下一个工作日的时间调节器实现。所有这些都已经在Stack Overflow上讨论过很多次了。因此,搜索以了解更多信息并查看示例代码。

类似未经测试的代码。 (使用风险自负。)

public Duration untilNextRun ( final ZoneId zoneId )
{
    // Calculate the amount to wait untli the next run of some task.
    // From Monday-Friday, 5 AM to 5 PM, we want to run every half-hour.
    // Outside those hours, we must wait until the next 5 AM starting time.

    Set < DayOfWeek > weekDays = EnumSet.of( DayOfWeek.MONDAY , DayOfWeek.TUESDAY , DayOfWeek.WEDNESDAY , DayOfWeek.THURSDAY , DayOfWeek.FRIDAY );
    LocalTime startTime = LocalTime.of( 5 , 0 );
    LocalTime stopTime = LocalTime.of( 17 , 0 );

    ZonedDateTime now = ZonedDateTime.now( zoneId );
    DayOfWeek currentDayOfWeek = now.getDayOfWeek();
    LocalDate currentDate = now.toLocalDate();
    LocalTime currentTime = now.toLocalTime();


    if ( weekDays.contains( currentDayOfWeek ) )
    {
        if ( currentTime.isBefore( startTime ) )
        {
            // On a weekday, but too early to start a regular 30-minute wait. Wait until 5 AM on this same date.
            ZonedDateTime zdt = ZonedDateTime.of( currentDate , startTime , zoneId );
            Duration duration = Duration.between( now.toInstant() , zdt.toInstant() );
            return Objects.requireNonNull( duration );
        } else if ( currentTime.isBefore( stopTime ) )
        {
            // On a weekday, within our working hours, so start a regular 30-minute wait.
            return Duration.ofMinutes( 30 );
        } else if ( ! currentTime.isBefore( stopTime ) )
        {
            // Too late in the day to start another 30-minute wait. So wait until the next non-weekend day.
            LocalDate nextWorkingLocalDate = currentDate.with( Temporals.nextWorkingDay() ) ;
            ZonedDateTime zdt = ZonedDateTime.of( nextWorkingLocalDate , startTime , zoneId );
            Duration duration = Duration.between( now.toInstant() , zdt.toInstant() );
            return Objects.requireNonNull( duration );
        } else
        {
            throw new IllegalStateException( "Should not have ever reached this point. Message # 7818816c-4b5c-47e3-a0a5-d7c8c999f071." );
        }
    } else
    {
        // Not on a weekday (on a weekend instead), so we cannot start another 30-minute wait. So wait until the next non-weekend day.
        LocalDate nextWorkingLocalDate = currentDate.with( Temporals.nextWorkingDay() ) ;
        ZonedDateTime zdt = ZonedDateTime.of( nextWorkingLocalDate , startTime , zoneId );
        Duration duration = Duration.between( now.toInstant() , zdt.toInstant() );
        return Objects.requireNonNull( duration );
    }
}

尝试使用该方法。

Duration duration = this.untilNextRun( ZoneId.of( "America/Los_Angeles" ) );

System.out.println( "ZonedDateTime.now(…) = " + ZonedDateTime.now( ZoneId.of( "America/Los_Angeles" ) ) );
System.out.println( "duration = " + duration );

ZonedDateTime.now(…)= 2020-04-06T18:29:41.880591-07:00 [America / Los_Angeles]

持续时间= PT10H30M18.124981S

要在计划的执行程序服务上计划任务,请将Duration转换为分钟或秒数。

ses.schedule( task , duration.toSeconds() , TimeUnit.SECONDS ) ;

作为该任务职责的一部分,它应该安排该任务的另一次执行。如果任务完成的时刻仍在目标时间内,请安排30分钟的延迟。如果结束时刻不在目标范围内,请安排足够长的延迟时间以使我们到达下一个开始的目标时间。更长的延迟可能是在M-F过夜或在周末。

将任务定义为RunnableCallable。像这样的未尝试代码。

Runnable task = ( ) -> {
    System.out.println( "Task is running at " + Instant.now() );
    //  … do your work …
    // Schedule task to run again.
    ZoneId z = ZoneId.of( "America/Montreal" );
    Duration d = this.untilNextRun( z );
    ses.schedule( task , d , TimeUnit.SECONDS ));
};