11

有时重复任务的持续时间比它的周期长(在我的例子中,这可能一次发生几个小时)。想想一个重​​复的任务,它需要 7 分钟运行,并且计划每 10 分钟运行一次,但有时连续几个小时每次运行需要 15 分钟。

Timer 和 ScheduledThreadPoolExecutor 类都有一个 scheduleAtFixedRate 方法,通常用于此类功能。但是,两者都具有“落后时努力追赶”的特点。换句话说,如果 Timer 落后于几次执行,它会建立一个工作队列,该队列将连续工作,直到它赶上如果没有任何任务花费超过指定期间。如果上一次运行未完成,我想通过跳过当前执行来避免这种行为。

我有一个解决方案,涉及搞乱池执行器的 afterExecution 方法,重新计算延迟,并用新的延迟重新安排可运行对象,但想知道是否有更简单的方法,或者这个功能是否已经存在于某个公共库中. 我知道以固定延迟而不是固定周期进行调度,但这对我不起作用,因为尝试在固定时间执行任务很重要。有没有比我的 afterExecution 解决方案更简单的选项?

4

3 回答 3

20

我认为您想要的是长时间运行的任务本身不在 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);
于 2012-08-14T02:26:08.057 回答
3

你可以改用scheduleWithFixedDelay方法。它很相似,但是这个没有错过运行的队列,而是仅在当前 Runnable 终止时才重新开始计数。

文档说明 Runnable的重新执行将根据延迟参数进行安排:

一次执行终止与下一次执行开始之间的延迟。

于 2020-04-17T07:47:10.653 回答
1

制作第三个班级,例如称为协调员。Coordinator 有一个同步的 startRunning() 方法,它将 isRunning 设置为 true,如果另一个线程尚未运行,则返回 true。还应该有一个同步的 stopRunning 方法,它将 isRunning 设置为 false。如果 runnable 已经在运行,则返回 true。您创建此类的单个实例并将引用传递给您构造的所有可运行文件。在 runnable 的 run 方法中,您首先调用 startRunning 并检查返回以验证另一个尚未运行。确保将代码放在 run() 中的 try-finally 中,并从 finally 块中调用 stopRunning。

于 2012-08-14T03:41:29.527 回答