在 java 中安排单线程重复可运行,但如果前一次运行未完成则跳过当前运行
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11944688/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Schedule a single-threaded repeating runnable in java, but skip the current run if previous run is not finished
提问by jonderry
Sometimes the duration of a repeated task is longer than its period (In my case, this can happen for hours at a time). Think of a repeated task that takes 7 minutes to run and is scheduled to run every 10 minutes, but sometimes takes 15 minutes for each run for a few hours in a row.
有时重复任务的持续时间比它的周期长(在我的情况下,这可能一次发生数小时)。想想一个重复的任务,它需要 7 分钟才能运行,并计划每 10 分钟运行一次,但有时连续几个小时每次运行需要 15 分钟。
The Timer and ScheduledThreadPoolExecutor classes both have a scheduleAtFixedRate method that is usually used for this type of functionality. However, both have the characteristic that they 'try to catch up when they fall behind'. In other words, if a Timer falls behind by a few executions, it builds up a queue of work that will be worked on continuously until it catches back up to the number of runs that would have happened if none of the tasks had taken longer than the specified period. I want to avoid this behavior by skipping the current execution if the previous run is not complete.
Timer 和 ScheduledThreadPoolExecutor 类都有一个 scheduleAtFixedRate 方法,通常用于此类功能。但是,两者都有“落后就追赶”的特点。换句话说,如果 Timer 落后于几次执行,它会建立一个工作队列,该队列将连续工作,直到它赶上如果没有任何任务花费的时间超过指定期间。如果上次运行未完成,我想通过跳过当前执行来避免这种行为。
I have one solution that involves messing around with the afterExecution method of a pooled executor, recalculating a delay, and rescheduling the runnable with the new delay, but was wondering if there's a simpler way, or if this functionality already exists in a common library somewhere. I know about scheduling with a fixed delay rather than a fixed period, but this will not work for me since it's important to try to execute the tasks at their fixed times. Are there any simpler options than my afterExecution solution?
我有一个解决方案,涉及处理池执行器的 afterExecution 方法,重新计算延迟,并使用新的延迟重新安排可运行,但想知道是否有更简单的方法,或者此功能是否已存在于某个公共库中. 我知道有固定延迟而不是固定时间段的调度,但这对我不起作用,因为尝试在固定时间执行任务很重要。有没有比我的 afterExecution 解决方案更简单的选择?
回答by Mark Peters
I think what you want is for the long-running task itself to not run in the ScheduledExecutorService itself, but in a background thread. Then the fixed-rate task will always complete quickly, since it is only used for checking whether to start the actual task in the background (or not, if it's still running from last time).
我认为您想要的是长时间运行的任务本身不在 ScheduledExecutorService 本身中运行,而是在后台线程中运行。那么固定速率任务将始终快速完成,因为它仅用于检查是否在后台启动实际任务(或者,如果它仍然从上次开始运行)。
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
final Runnable actualTask = null;
executorService.scheduleAtFixedRate(new Runnable() {
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private Future<?> lastExecution;
@Override
public void run() {
if (lastExecution != null && !lastExecution.isDone()) {
return;
}
lastExecution = executor.submit(actualTask);
}
}, 10, 10, TimeUnit.MINUTES);
回答by Sarel Botha
Make a third class, say called Coordinator. Coordinator has a synchronized startRunning() method which sets isRunning to true and returns true if another thread was not running already. There should also be a synchronized stopRunning method which sets isRunning to false. It returns true if a runnable is already running. You make a single instance of this class and pass a reference to all of the runnables you construct. In the runnable's run method you first call startRunning and check the return to verify that another one isn't running already. Make sure to put the code in run() in a try-finally and call stopRunning from within the finally block.
做第三个类,比如说叫做 Coordinator。Coordinator 有一个同步的 startRunning() 方法,该方法将 isRunning 设置为 true,如果另一个线程尚未运行,则返回 true。还应该有一个同步的 stopRunning 方法,它将 isRunning 设置为 false。如果 runnable 已经在运行,则返回 true。您创建此类的单个实例并传递对您构造的所有可运行对象的引用。在 runnable 的 run 方法中,您首先调用 startRunning 并检查返回以验证另一个尚未运行。确保将 run() 中的代码放入 try-finally 并从 finally 块中调用 stopRunning。
回答by DanielCuadra
You could use scheduleWithFixedDelay
method instead. It's similar but this one does not have a queue for missed runs and instead starts counting again only when the current Runnable was terminated.
你可以改用scheduleWithFixedDelay
方法。它是相似的,但是这个没有错过运行的队列,而是仅在当前 Runnable 终止时才重新开始计数。
The documentationstates the reexecution of the Runnable will be scheduled based on the delay parameter:
该文档的状态了Runnable的再执行将根据延迟参数安排:
The delay between the termination of one execution and the commencement of the next.
一个执行终止和下一个执行开始之间的延迟。