Java:从scheduleAtFixedRate获取未来的方法

时间:2015-11-25 08:47:32

标签: java multithreading

请考虑以下代码:

public static void main(String ... args) throws InterruptedException {
  ScheduledExecutorService threadsPool = Executors.newSingleThreadScheduledExecutor();
  Future<?> f = threadsPool.scheduleAtFixedRate(() -> {
    try {
      Thread.sleep(1);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }, 0, 2, TimeUnit.SECONDS);

  Thread.sleep(5000);
  threadsPool.shutdown();
  System.out.println(threadsPool.awaitTermination(1, TimeUnit.SECONDS));
  try {
    f.get();
  } catch (Exception e) {
    e.printStackTrace();
  }
}

注意邪恶线程的睡眠时间:1ms。这可以保证在线程池等待下一次迭代并且邪恶线程没有运行时关闭 这会导致get()方法出现CancellationException:如果我理解正确,ScheduledExecutorService在调用shutdown()时会取消任何挂起的任务,所以这种行为是有道理的。

接下来我将邪恶线程的休眠时间从1改为1999.这可以保证关闭将在邪恶线程的睡眠期间。 这导致get()方法永远等待。

我的下一个问题是为什么会发生这种情况?调用shutdown将正常关闭服务。实际上,邪恶的线程完成了迭代并且没有再次开始 但为什么get()方法没有返回?我是否误解了ScheduledFuture上的get()方法?
我认为只要邪恶的线程完成,并且池关闭,get()方法应该返回null。

1 个答案:

答案 0 :(得分:1)

如果未来未完成,则有可能由shutdown方法调用future.cancel()方法(或执行程序以某种方式取消了将来)。 这是请求执行程序关闭的预期行为,因为它不等待任务完成。

如果在完成之前取消了未来,则抛出CancellationException是预期的行为。否则它将等待以太任务返回。

在这种情况下,由于您使用ScheduledExecutorService,因此在这种情况下您可以使用ScheduledFuture代替Future来获取更多信息。 https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledFuture.html 这样您就可以访问getDelay界面提供的Delayed方法。 https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Delayed.html

如果您还检查方法scheduleAtFixedRate的源代码,您会看到它实际上创建了ScheduledFutureTask。见下面的代码。 (从来源复制。)

/**
     * @throws RejectedExecutionException {@inheritDoc}
     * @throws NullPointerException       {@inheritDoc}
     * @throws IllegalArgumentException   {@inheritDoc}
     */
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit) {
        if (command == null || unit == null)
            throw new NullPointerException();
        if (period <= 0)
            throw new IllegalArgumentException();
        ScheduledFutureTask<Void> sft =
            new ScheduledFutureTask<Void>(command,
                                          null,
                                          triggerTime(initialDelay, unit),
                                          unit.toNanos(period));
        RunnableScheduledFuture<Void> t = decorateTask(command, sft);
        sft.outerTask = t;
        delayedExecute(t);
        return t;
    }

ScheduledFutureTask的{​​{1}}方法会在完成后立即自动重新执行。 (参见最后一行源代码)。

run

/** * Overrides FutureTask version so as to reset/requeue if periodic. */ public void run() { boolean periodic = isPeriodic(); if (!canRunInCurrentRunState(periodic)) cancel(false); else if (!periodic) ScheduledFutureTask.super.run(); else if (ScheduledFutureTask.super.runAndReset()) { setNextRunTime(); reExecutePeriodic(outerTask); } } outerTask所以它实际上是ScheduledFutureTask本身。所以它永远不会完整。因此,您实际上永远不会this结果。