因此,Oracle itself提出的关于关闭ExecutorService
的最佳做法如下:
@PreDestroy
public void cleanUp(){
executorService.shutdown();
try {
if (executorService.awaitTermination(TIMEOUT, TimeUnit.MILLISECONDS)) {
executorService.shutdownNow();
}
} catch (InterruptedException e) {
executorService.shutdownNow();
}
}
这也将负责取消任何待处理的任务。我想知道上面的代码片段何时优先于以下内容:
private List<Future> tasks = Collections.EMPTY_LIST;
public void doStuff(){
for (Service service : services) {
Future<?> future = executorService.submit(()->service.update(args));
tasks.add(task);
}
}
@PreDestroy
public void cleanUp() {
for (Future task : tasks) {
task.cancel(false);
}
}
后者将允许运行任务完成而不会中断它们(tasks.cancel(false)
)。在这种方法中没有超时,因此任务中的无限循环将阻止应用程序停止。此外,我们还剩下一个仍在运行的执行程序服务:但如果确定在取消完成之后没有其他任务可以提交,我们是否应该真的关心这个问题?
对于在进行实际关闭之前想要等待作业任务的终止/完成的情况,我最感兴趣的是最佳实践。为什么我们真的关心执行程序服务本身的关闭?
Imho,有一个将在应用程序关闭时取消的期货列表是一个更清洁的解决方案,因为可以选择哪些中断以及哪些等待完成。
对此更加精细的见解非常受欢迎。
这些都是Spring bean的一部分,你可以从用于指定@PotsDestroy
方法的cleanUp
注释中推断出它是一个用于清理执行程序服务任务的关闭钩子。
在建议的解决方案中,应在shutdownNow()方法调用之后调用wait。
应关闭未使用的ExecutorService以允许回收其资源。
shutdownNow时(): -
Future.cancel(false): -
简短说明和建议: -
List
对象本身并不是线程安全的在内部,执行者服务实现类(如ThreadPoolExecutor,ScheduledThreadPoolExecutor等)负责处理所有线程安全问题。它使用BlockingQueue
来克服线程安全问题。此外,它相应地处理异常和不同的状态。您可能需要了解所有内部(即幕后发生的事情)以提出一个好的自定义解决方案。
最简单和最好的方法是使用执行程序服务实现中可用的标准实现(ThreadPoolExecutor,ScheduledThreadPoolExecutor等)来避免任何不利影响(即内存泄漏,线程安全问题)。