简体   繁体   中英

Run a task every 30 min during time window

I am quite new to java, but I am trying to work out running a task every 30 min during the hours 5 AM to 5 PM during the week. I looked around and found that I could possible use this class

  ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();

I am thinking I could use the library to call a function and wrap the function call in an if statement to check if it falls between the 9 to 5 time. My only issue about this is running a thread thru out the night while it is not doing anything. Was wondering if that is something I should worry about or is there a way I can just have the thread sleep after its last run for 8 or so hours until the next day

Yes, use a ScheduledExecutorService .

Notice how the schedule method takes a pair of arguments for an amount and a granularity of an amount of time to wait before running the task.

So when you start your app, see how long until when you want to start the first task. Use DayOfWeek to see if the current moment is Monday-Friday. Check to see if the current LocalTime is between 5 AM to 5 PM. Use ZonedDateTime to determine moments.

To get the next working day, add the ThreeTen-Extra library to your project to get its next-working-day temporal adjuster implementation. All this has been covered many times already on Stack Overflow. So search to learn more and see example code.

Something like this untested code. (Use at your own risk.)

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

Try using that method.

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]

duration = PT10H30M18.124981S

To schedule your task on the scheduled executor service, convert the Duration to a number of minutes or seconds.

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

As part of that task's duties, it should schedule another execution of the task. If the moment when the task finishes is still within the target hours, schedule a delay of 30 minutes. If the finishing moment is outside the target, schedule with a delay long enough to take us up to the next starting target time. That longer delay might be overnight for MF, or over the weekend.

Define your task as a Runnable or Callable . Something like this untried code.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM