Java 中 ScheduledExecutorService 的时间一致性问题
Time consistency issue with ScheduledExecutorService in Java
我正在尝试使用 ScheduledExecutorService 每 15 分钟调用一次 运行nable 任务。这些任务应该在以下时间每小时 运行:
第 2 分钟 10 秒
第 17 分 10 秒
第 32 分 10 秒
第 47 分第 10 秒。
我使用 Calendar 实例计算第一次执行任务的初始延迟,然后使用 scheduleAtFixedRate() 的延迟每 15 分钟执行一次任务。这在最初几天工作正常,但几天后我可以观察到任务执行的时间偏移。例如,本应在 12:02:10 开始的任务却在 12:03:45 被调用。我哪里出错了?
public static void main(String[] args) {
final ScheduledExecutorService scheduler = Executors
.newScheduledThreadPool(15);
Date aDate = new Date();
Calendar cal = Calendar.getInstance();
cal.setTime(aDate);
int currentHour = cal.get(Calendar.HOUR_OF_DAY);
int currentMins = cal.get(Calendar.MINUTE);
int currentSecs = cal.get(Calendar.SECOND);
int unitInSecs;
int delayFor15MinTasksInSecs;
unitInSecs = currentMins * 60 + currentSecs;
delayFor15MinTasksInSecs = unitInSecs < (3 * 60 - 50 ) ? (3 * 60 - 50 ) - unitInSecs : unitInSecs < (18 * 60 - 50 ) ? (18 * 60 - 50 ) - unitInSecs : unitInSecs < (33 * 60 - 50 ) ? (33 * 60 - 50 ) - unitInSecs : unitInSecs < (48 * 60 - 50 )? (48 * 60 - 50 ) - unitInSecs : (63 *60 - 50 ) - unitInSecs;
scheduler.scheduleAtFixedRate(new RTPAcquiringTask(10), delayFor15MinTasksInSecs, 15*60, TimeUnit.SECONDS);
}
API 在 time-keeping 中并不完美。
来自 ScheduledExecutorService
的 Javadoc:
Beware however that expiration of a relative delay need not coincide with the current Date at which the task is enabled due to network time synchronization protocols, clock drift, or other factors.
不是Real-Time
作为 and its comments say, conventional Java implementations such as Oracle’s or the OpenJDK project are not real-time systems. So they do not keep time perfectly. Classes such as ScheduledExecutorService
are not intended to keep time perfectly. If need be, look into the attempts at making a real-time Java.
解决方法
我还没有尝试过这个解决方法来最小化偏差;只是一个想法。
作为一种解决方法,您可以拥有一个额外的后台线程,其工作是偶尔终止您当前的 ScheduledExecutorService
工作,并开始一个具有 re-calculated 延迟数的新工作。你说漂移在几天后变得明显,所以也许 terminate-and-reschedule 每天做一次苦差事 运行。
您可以通过捕获 ScheduledFuture
object being returned by your call to scheduleAtFixedRate
. Currently your call ignores and loses that returned object. Keep track of it, make it available to the background thread I suggest. Call cancel
来跟踪和终止 ScheduledExecutorService
工作。
我正在尝试使用 ScheduledExecutorService 每 15 分钟调用一次 运行nable 任务。这些任务应该在以下时间每小时 运行: 第 2 分钟 10 秒 第 17 分 10 秒 第 32 分 10 秒 第 47 分第 10 秒。
我使用 Calendar 实例计算第一次执行任务的初始延迟,然后使用 scheduleAtFixedRate() 的延迟每 15 分钟执行一次任务。这在最初几天工作正常,但几天后我可以观察到任务执行的时间偏移。例如,本应在 12:02:10 开始的任务却在 12:03:45 被调用。我哪里出错了?
public static void main(String[] args) {
final ScheduledExecutorService scheduler = Executors
.newScheduledThreadPool(15);
Date aDate = new Date();
Calendar cal = Calendar.getInstance();
cal.setTime(aDate);
int currentHour = cal.get(Calendar.HOUR_OF_DAY);
int currentMins = cal.get(Calendar.MINUTE);
int currentSecs = cal.get(Calendar.SECOND);
int unitInSecs;
int delayFor15MinTasksInSecs;
unitInSecs = currentMins * 60 + currentSecs;
delayFor15MinTasksInSecs = unitInSecs < (3 * 60 - 50 ) ? (3 * 60 - 50 ) - unitInSecs : unitInSecs < (18 * 60 - 50 ) ? (18 * 60 - 50 ) - unitInSecs : unitInSecs < (33 * 60 - 50 ) ? (33 * 60 - 50 ) - unitInSecs : unitInSecs < (48 * 60 - 50 )? (48 * 60 - 50 ) - unitInSecs : (63 *60 - 50 ) - unitInSecs;
scheduler.scheduleAtFixedRate(new RTPAcquiringTask(10), delayFor15MinTasksInSecs, 15*60, TimeUnit.SECONDS);
}
API 在 time-keeping 中并不完美。
来自 ScheduledExecutorService
的 Javadoc:
Beware however that expiration of a relative delay need not coincide with the current Date at which the task is enabled due to network time synchronization protocols, clock drift, or other factors.
不是Real-Time
作为 ScheduledExecutorService
are not intended to keep time perfectly. If need be, look into the attempts at making a real-time Java.
解决方法
我还没有尝试过这个解决方法来最小化偏差;只是一个想法。
作为一种解决方法,您可以拥有一个额外的后台线程,其工作是偶尔终止您当前的 ScheduledExecutorService
工作,并开始一个具有 re-calculated 延迟数的新工作。你说漂移在几天后变得明显,所以也许 terminate-and-reschedule 每天做一次苦差事 运行。
您可以通过捕获 ScheduledFuture
object being returned by your call to scheduleAtFixedRate
. Currently your call ignores and loses that returned object. Keep track of it, make it available to the background thread I suggest. Call cancel
来跟踪和终止 ScheduledExecutorService
工作。