请考虑以下代码:
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。
答案 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
结果。