我对Java还是很陌生,但是我正尝试在一周的凌晨5点至下午5点之间每30分钟执行一次任务。我环顾四周,发现可以使用此类
ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();
我想我可以使用该库来调用函数,并将函数调用包装在if语句中,以检查它是否介于9到5时间之间。关于此的我唯一的问题是在整个晚上不执行任何操作时运行线程。想知道这是否是我应该担心的事情,还是有一种方法可以让线程在上次运行8个小时后直到第二天进入睡眠状态
答案 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过夜或在周末。
将任务定义为Runnable
或Callable
。像这样的未尝试代码。
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 ));
};