考虑以下代码:
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();
}
}
注意邪恶线程的 hibernate 时间:1ms。这保证了关闭将在线程池等待下一次迭代并且邪恶线程未运行时发生。
这会导致 get() 方法出现 CancellationException:如果我理解正确,ScheduledExecutorService 在调用 shutdown() 时会取消任何未决任务,因此这种行为是有道理的。
接下来我将邪恶线程的 hibernate 时间从1更改为1999。这保证了关闭将在邪恶线程 hibernate 期间。
这会导致永远等待 get() 方法。
我的下一个问题是为什么会发生这种行为?调用 shutdown 将优雅地关闭服务。事实上,邪恶的线程完成了迭代,并没有再次启动。
但是为什么get()方法没有返回呢?我是否误解了 ScheduledFuture 的 get() 方法?
我认为只要邪恶线程结束,池关闭,get() 方法就应该返回 null。
最佳答案
如果 future 没有完成,那么 future.cancel()
方法可能被 shutdown 方法调用(或者执行者以某种方式取消了 future)。
这是请求执行程序关闭的预期行为,因为它不会等待任务完成。
如果 future 在完成之前被取消,则抛出 CancellationException
是预期的行为。否则它会等待以太币让任务返回。
在这种情况下,由于您使用 ScheduledExecutorService
,因此在这种情况下您可以使用 ScheduledFuture
而不是 Future
来获取更多信息。 https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledFuture.html
这将允许您访问 Delayed
接口(interface)提供的 getDelay
方法。
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
的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
是 this
所以它实际上是 ScheduledFutureTask 本身。所以它永远不会完整。由于这个原因,您实际上永远无法得到
结果。
关于Java:从 scheduleAtFixedRate 获取 future 的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33911951/